"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > تشغيل Discord Bot على Raspberry Pi

تشغيل Discord Bot على Raspberry Pi

تم النشر بتاريخ 2024-11-07
تصفح:248

صورة الغلاف لدانيال تافجورد على Unsplash

لقد أكملت مؤخرًا معسكرًا تدريبيًا لهندسة البرمجيات، وبدأت العمل على أسئلة LeetCode السهلة وشعرت أنه سيساعدني في إبقائي مسؤولاً إذا كان لدي تذكير يومي لحل الأسئلة. قررت تنفيذ ذلك باستخدام برنامج Discord bot الذي يعمل وفقًا لجدول زمني مدته 24 ساعة (على جهاز Raspberry pi الموثوق به بالطبع) والذي سيقوم بما يلي:

  • انتقل إلى بنك بيانات محدد مسبقًا لأسئلة Leetcode السهلة
  • احصل على سؤال لم يتم نشره على قناة الديسكورد
  • انشر سؤال leetcode كموضوع في قناة الديسكورد (حتى تتمكن من إضافة الحل الخاص بك بسهولة)
  • تم وضع علامة على السؤال على أنه منشور لتجنب نشره على القناة مرة أخرى

Running a Discord Bot on Raspberry Pi

أدرك أنه قد يكون من الأسهل الذهاب إلى LeetCode وحل سؤال يوميًا ولكن علي أن أتعلم الكثير عن Python وDiscord بمساعدة ChatGPT في هذا المشروع الصغير. هذه أيضًا محاولتي الأولى للرسم، لذا يرجى التحمل lol

Running a Discord Bot on Raspberry Pi

يثبت

1. استخدام بيئة بايثون الافتراضية
2. تثبيت التبعيات
3. إعداد قاعدة بيانات Leetcode للأسئلة السهلة
4. إعداد متغيرات البيئة
5. إنشاء تطبيق Discord
6. قم بتشغيل الروبوت!

1. استخدم بيئة بايثون الافتراضية

أوصي باستخدام بيئة بايثون الافتراضية لأنه عندما اختبرت ذلك في البداية على Ubuntu 24.04، واجهت الخطأ أدناه

Running a Discord Bot on Raspberry Pi

إعداده سهل نسبيًا، فقط قم بتشغيل الأوامر التالية وها أنت في بيئة بايثون الافتراضية!

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

2. تثبيت التبعيات

التبعيات التالية مطلوبة:

  • AWS CLI

قم بتثبيت AWS CLI عن طريق تشغيل ما يلي:

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

ثم قم بتشغيل awsconfig لإضافة بيانات الاعتماد المطلوبة. راجع تكوين مستند AWS CLI.

  • تبعيات النقطة

يمكن تثبيت تبعيات النقطة التالية مع ملف المتطلبات عن طريق تشغيل pip install -r Requirements.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. قم بإعداد قاعدة بيانات الأسئلة السهلة Leetcode

كان Letscrape أمرًا حيويًا لهذه الخطوة. لمعرفة المزيد حول هذا الموضوع، راجع مستندات Leetscrape.
أريد فقط العمل على أسئلة leetcode السهلة (بالنسبة لي، فهي صعبة للغاية) لذلك قمت بما يلي:

  • احصل على قائمة بجميع الأسئلة من leetcode باستخدام leetscrape واحفظ القائمة في ملف 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")
  • أنشئ جدول Amazon DynamoDB واملأه بقائمة من الأسئلة السهلة التي تمت تصفيتها من ملف CSV المحفوظة في الخطوة السابقة.
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. إعداد متغيرات البيئة

إنشاء ملف .env لتخزين متغيرات البيئة

DISCORD_BOT_TOKEN=*****

5. إنشاء تطبيق Discord

اتبع الإرشادات الموجودة في مستندات Discord Developer لإنشاء تطبيق Discord وروبوت بأذونات كافية. تأكد من تفويض الروبوت بأذونات OAuth التالية على الأقل:

  • إرسال الرسائل
  • إنشاء مواضيع عامة
  • إرسال الرسائل في المواضيع

6. قم بتشغيل الروبوت!

يوجد أدناه رمز البوت الذي يمكن تشغيله باستخدام أمر 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)

هناك خيارات متعددة لتشغيل البوت. في الوقت الحالي، أقوم فقط بتشغيل هذا في tmux shell ولكن يمكنك أيضًا تشغيله في حاوية إرساء أو على VPC من AWS أو Azure أو DigitalOcean أو موفري الخدمات السحابية الآخرين.

الآن علي فقط أن أحاول حل أسئلة Leetcode...

بيان الافراج تم إعادة نشر هذه المقالة على: https://dev.to/beretests/running-a-discord-bot-on-raspberry-pi-4la4?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3