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:
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
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!
Ich empfehle die Verwendung einer virtuellen Python-Umgebung, da ich beim ersten Test unter Ubuntu 24.04 auf den folgenden Fehler gestoßen bin
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
Die folgenden Abhängigkeiten sind erforderlich:
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.
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
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:
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")
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)
Erstellen Sie eine .env-Datei zum Speichern von Umgebungsvariablen
DISCORD_BOT_TOKEN=*****
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:
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!