Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Menjalankan Bot Discord pada Raspberry Pi

Menjalankan Bot Discord pada Raspberry Pi

Susan Sarandon
Susan Sarandonasal
2024-10-01 12:11:02599semak imbas

Foto Muka Depan oleh Daniel Tafjord di Unsplash

Baru-baru ini saya telah melengkapkan bootcamp kejuruteraan perisian, mula mengerjakan soalan mudah LeetCode dan merasakan ia akan membantu memastikan saya bertanggungjawab jika saya mempunyai peringatan setiap hari untuk menyelesaikan soalan. Saya memutuskan untuk melaksanakan ini menggunakan bot perselisihan yang berjalan pada jadual 24 jam (tentu saja pada raspberry pi yang boleh dipercayai) yang akan melakukan perkara berikut:

  • pergi ke bank data pratakrif soalan leetcode mudah
  • dapatkan soalan yang belum disiarkan ke saluran perselisihan
  • siarkan soalan leetcode sebagai urutan dalam saluran perselisihan (supaya anda boleh menambah penyelesaian anda dengan mudah)
  • soalan ditandakan sebagai disiarkan untuk mengelak menyiarkannya ke saluran lagi

Running a Discord Bot on Raspberry Pi

Saya sedar mungkin lebih mudah untuk pergi ke LeetCode sahaja dan menyelesaikan soalan sehari tetapi saya dapat belajar banyak tentang Python dan Discord dengan bantuan daripada ChatGPT pada projek mini ini. Ini juga percubaan pertama saya membuat lakaran jadi harap bersabar lol

Running a Discord Bot on Raspberry Pi

Persediaan

1. Gunakan persekitaran maya python
2. Pasang kebergantungan
3. Sediakan pangkalan data soalan mudah Leetcode
4. Sediakan pembolehubah persekitaran
5. Cipta apl Discord
6. Jalankan Bot!

1. Gunakan persekitaran maya python

Saya mengesyorkan penggunaan persekitaran maya python kerana pada mulanya saya menguji ini pada Ubuntu 24.04, saya mengalami ralat di bawah

Running a Discord Bot on Raspberry Pi

Menyediakannya agak mudah, cuma jalankan arahan berikut dan voila, anda berada dalam persekitaran maya ular sawa!

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

2. Pasang kebergantungan

Kebergantungan berikut diperlukan:

  • AWS CLI

Pasang AWS CLI dengan menjalankan yang berikut:

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

Kemudian jalankan konfigurasi aws untuk menambah bukti kelayakan yang diperlukan. Lihat Konfigurasikan dokumen AWS CLI.

  • kebergantungan pip

Kebergantungan pip berikut boleh dipasang dengan fail keperluan dengan menjalankan 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. Sediakan pangkalan data soalan mudah leetcode

Leetscrape adalah penting untuk langkah ini. Untuk mengetahui lebih lanjut mengenainya, lihat dokumen Leetscrape.
Saya hanya mahu mengerjakan soalan mudah leetcode (pada saya, ia agak sukar) jadi saya melakukan perkara berikut:

  • dapatkan senarai semua soalan daripada leetcode menggunakan leetscrape dan simpan senarai ke 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")
  • buat jadual Amazon DynamoDB dan isikannya dengan senarai soalan mudah yang ditapis daripada csv yang disimpan dalam langkah sebelumnya.
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. Sediakan pembolehubah persekitaran

Buat fail .env untuk menyimpan pembolehubah persekitaran

DISCORD_BOT_TOKEN=*****

5. Cipta apl Discord

Ikuti arahan dalam dokumen Pembangun Discord untuk membuat apl dan bot Discord dengan kebenaran yang mencukupi. Pastikan anda membenarkan bot dengan sekurang-kurangnya kebenaran OAuth berikut:

  • Hantar Mesej
  • Buat Benang Awam
  • Hantar Mesej dalam Thread

6. Jalankan Bot!

Di bawah ialah kod untuk bot yang boleh dijalankan dengan perintah 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)

Terdapat berbilang pilihan untuk menjalankan bot. Buat masa ini, saya hanya menjalankan ini dalam shell tmux tetapi anda juga boleh menjalankan ini dalam bekas docker atau pada VPC daripada AWS, Azure, DigitalOcean atau penyedia awan lain.

Sekarang saya hanya perlu cuba menyelesaikan soalan Leetcode...

Atas ialah kandungan terperinci Menjalankan Bot Discord pada Raspberry Pi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn