discord-inteactionとは?
discord公式が出してるhttp経由のdiscord-bot製法
メリット
flaskとかで動くから常時shellを動かす必要がない、しかもawsとかでもできる そのれいがcyclic.shでここの記事にあるようにdiscord-interactionのおかげで24時間動かせたりする。
デメリット
特にないが埋め込みでvideoオプションが使えなかったり、discord.py やdiscord.jsと少し書き方が異なる点 ※もちろんdiscord.py や discord.jsでないのでdiscord.pyやjsの機能は使えないです
早速サンプル
from flask import Flask, request, send_file import os app = Flask(__name__) @app.route('/', methods=['GET']) if __name__ == '__main__': app.run()
これは普通のflaskappですがここに
import yt_dlp from flask import Flask, request, jsonify, send_file from flask_cors import CORS import requests import os from discord_interactions import InteractionType, InteractionResponseType, verify_key_decorator app = Flask(__name__) CORS(app) import subprocess import json PUBLIC_KEY = os.getenv('PUBLIC_KEY') CLIENT_ID = os.getenv('CLIENT_ID') DISCORD_TOKEN = os.getenv('DISCORD_TOKEN') @app.route('/interactions', methods=['POST']) @verify_key_decorator(PUBLIC_KEY) def interactions(): # JSONデータを取得 data = request.json print(data) # インタラクションの種類を取得 interaction_type = data["type"] # PINGの場合はPONGを返す if interaction_type == 1: response_ping = {"type": 1} requests.post(f"https://discord.com/api/v9/interactions/{data['id']}/{data['token']}/callback", json=response_ping, headers={"Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json"}) # APPLICATION_COMMANDの場合は遅延レスポンスを返す elif interaction_type == 2: # 遅延レスポンスを返す response_data = {"type": 5} response = requests.post(f"https://discord.com/api/v9/interactions/{data['id']}/{data['token']}/callback", json=response_data, headers={"Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json"}) command = data["data"]["name"] if command == "yt-ogp": ipturl = data["data"]["options"][0]["value"] media_type = data["data"]["options"][1]["value"] try: # yt-dlpを使用してURLを取得 ydl_opts = {'format': 'best', 'no_cache': True} if media_type == 'audio': ydl_opts['format'] = 'bestaudio' ydl_opts['extract_audio'] = True with yt_dlp.YoutubeDL(ydl_opts) as ydl: result = ydl.extract_info(ipturl, download=False) if 'url' in result: video_url = result['url'] thumbnail = result.get('thumbnail') title = result.get('title') description = result.get('description', '')[:100] # descriptionを取得し、最大100文字までに制限する uploader = result.get('uploader') uploader_url = result.get('uploader_url') # Embedを作成 embed = { "type": "link", "title": title, "description": description, "url": video_url, "color": 15548997, "image": {"url": thumbnail}, "author": {"name": uploader, "url": uploader_url} } # メッセージを送信 message_data = {"embeds": [embed]} headers = { "Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json" } requests.patch(f"https://discord.com/api/v9/webhooks/{CLIENT_ID}/{data['token']}/messages/@original", json=message_data, headers=headers) return '', 200 except Exception as e: print('Error processing interaction:', e) return 'Error processing interaction', 500 return '', 200 @app.route('/register-commands', methods=['GET']) def register_commands(): print('Received command registration request') commands = [ { "name": "yt-ogp", "description": "Fetch information from YouTube URL", "options": [ { "name": "url", "description": "YouTube URL", "type": 3, "required": True }, { "name": "type", "description": "Type of content (video or audio)", "type": 3, "required": True, "choices": [ {"name": "動画", "value": "video"}, {"name": "音楽", "value": "audio"} ] } ] } ] try: response = requests.put(f"https://discord.com/api/v9/applications/{CLIENT_ID}/commands", json=commands, headers={"Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json"}) if response.status_code == 200: print('Commands registered:', response.json()) return 'Commands have been registered' except Exception as e: print('Error registering commands:', e) return 'Error registering commands', 500 return '', 200 if __name__ == '__main__': app.run(debug=False)
これがあるflaskアプリですが、解説します。
※ここにflask-corsがありますが、これはなくても動きますがつけることをお勧めします ※ちゃんとenvも設定してくださいね
まず/inteactionsエンドポイントですが、
PINGリクエストならPONGと返してます。(これはないと動きません) なぜなら、discordのセキュリティーで、正しくレスポンスができるかどうかを確かめるためです。 さらにdiscord-interactionsには、@verify_key_decorator(PUBLIC_KEY)これを指定しないと、discordと通信できません。(認証関係ですね)
※verify_key_decoratorをimportから持ってきてくださいね
from discord_interactions import InteractionType, InteractionResponseType, verify_key_decorator
これで認証はできそうですね!!
次に、 そしてアプリケーションインタラクションだったらという処理がありますが、 まずDEFFERレスポンスを出しています。 これは遅延レスポンスをしますよーというもので
if interaction_type == 1: response_ping = {"type": 1} requests.post(f"https://discord.com/api/v9/interactions/{data['id']}/{data['token']}/callback", json=response_ping, headers={"Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json"}) # APPLICATION_COMMANDの場合は遅延レスポンスを返す elif interaction_type == 2: # 遅延レスポンスを返す response_data = {"type": 5} response = requests.post(f"https://discord.com/api/v9/interactions/{data['id']}/{data['token']}/callback", json=response_data, headers={"Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json"})
これはPINGならPONGのあとにありますが、 type2とはアプリケーションコマンドのレスポンスのことです。
このときにまずpostで、ちょっと待った!! て感じで送る感じです。
つまりあとで(厳密には15分以内に)PATCHで後ほどのapiにpatchすればメッセージが送ることができます
コードの例
requests.patch(f"https://discord.com/api/v9/webhooks/{CLIENT_ID}/{data['token']}/messages/@original", json=message_data, headers=headers)
これで後で送るとメッセージが確定されます
これで大体よさそうですね!!