実現したいこと
discord.js v14とX API v2にて、discord側でスラッシュコマンドを実行すると特定のハッシュタグがついたtwitterの投稿を取得しdiscordでランダムに返すようにしたいので、そのうえで発生したエラーを解消したいです。
発生している問題・分からないこと
twitterの投稿を取得するにあたり以下のエラーが発生しました。
(なおError fetching tweetsと[]とNo tweets found.はプログラム側で追加されたものになります。)
エラーメッセージ
error
1Error fetching tweets: ApiResponseError: Request failed with code 403 2 at RequestHandlerHelper.createResponseError (/rbd/pnpm-volume/d9af4806-06c4-4055-abaa-95683c0ef89c/node_modules/twitter-api-v2/dist/cjs/client-mixins/request-handler.helper.js:104:16) 3 at RequestHandlerHelper.onResponseEndHandler (/rbd/pnpm-volume/d9af4806-06c4-4055-abaa-95683c0ef89c/node_modules/twitter-api-v2/dist/cjs/client-mixins/request-handler.helper.js:262:25) 4 at Gunzip.emit (node:events:526:28) 5 at endReadableNT (node:internal/streams/readable:1345:12) 6 at processTicksAndRejections (node:internal/process/task_queues:83:21) { 7 error: true, 8 type: 'response', 9 code: 403, 10 headers: { 11 date: 'Sun, 24 Mar 2024 04:22:10 UTC', 12 perf: '7469935968', 13 server: 'tsa_b', 14 'set-cookie': [ 15 'guest_id=v1%3A171125413004808473; Max-Age=34214400; Expires=Thu, 24 Apr 2025 04:22:10 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None' 16 ], 17 'api-version': '2.95', 18 'content-type': 'application/json; charset=utf-8', 19 'cache-control': 'no-cache, no-store, max-age=0', 20 'content-length': '328', 21 'x-access-level': 'read', 22 'x-frame-options': 'SAMEORIGIN', 23 'content-encoding': 'gzip', 24 'x-transaction-id': '31ed000a8d001098', 25 'x-xss-protection': '0', 26 'x-rate-limit-limit': '40000', 27 'x-rate-limit-reset': '1711254949', 28 'content-disposition': 'attachment; filename=json.json', 29 'x-content-type-options': 'nosniff', 30 'x-rate-limit-remaining': '39998', 31 'strict-transport-security': 'max-age=631138519', 32 'x-response-time': '13', 33 'x-connection-hash': '27dc75c45ab2f6430c55e036d053de80dd26706b33e085d311414c8dd1154438', 34 connection: 'close' 35 }, 36 rateLimit: { limit: 40000, remaining: 39998, reset: 1711254949 }, 37 data: { 38 client_id: '27837829', 39 detail: 'When authenticating requests to the Twitter API v2 endpoints, you must use keys and tokens from a Twitter developer App that is attached to a Project. You can create a project via the developer portal.', 40 registration_url: 'https://developer.twitter.com/en/docs/projects/overview', 41 title: 'Client Forbidden', 42 required_enrollment: 'Appropriate Level of API Access', 43 reason: 'client-not-enrolled', 44 type: 'https://api.twitter.com/2/problems/client-forbidden' 45 } 46} 47[] 48No tweets found.
該当のソースコード
Javascript
1const fs = require('fs'); 2const path = require('path'); 3const { Intents } = require('discord.js'); 4const { SlashCommandBuilder } = require('@discordjs/builders'); 5const TwitterApi = require('twitter-api-v2').TwitterApi; 6require('dotenv').config({ path: '../.env' }); 7const Discord = require("discord.js"); 8 const { 9 Client, 10 GatewayIntentBits: { 11 Guilds, 12 GuildMessages, 13 MessageContent 14 }, 15 Collection, 16 Events, 17 GatewayIntentBits, 18 Partials, 19 Permissions 20 } = require("discord.js"); 21const client = new Discord.Client({ 22 'intents': [GatewayIntentBits.Guilds, 23 GatewayIntentBits.GuildMembers, 24 GatewayIntentBits.GuildEmojisAndStickers, 25 GatewayIntentBits.GuildIntegrations, 26 GatewayIntentBits.GuildVoiceStates, 27 GatewayIntentBits.GuildPresences, 28 GatewayIntentBits.GuildMessages, 29 GatewayIntentBits.GuildMessageReactions, 30 GatewayIntentBits.DirectMessages, 31 GatewayIntentBits.DirectMessageReactions, 32 GatewayIntentBits.MessageContent], 33 'partials': [Partials.User, 34 Partials.Channel, 35 Partials.GuildMember, 36 Partials.Message, 37 Partials.Reaction] 38}); 39 40const appOnlyClient = new TwitterApi(process.env.bearerToken); 41const v2Client = client.v2; 42 43const { EmbedBuilder } = require('discord.js'); 44 45async function getTweetsWithHashtag(hashtag) { 46 try { 47 const tweets = await appOnlyClient.get(`https://api.twitter.com/2/tweets/search/recent?query=%23(タグ名)&max_results=10`); 48 console.log('Tweets:', tweets.data); 49 return tweets.data; // Twitter APIからのレスポンスを返す 50 } catch (error) { 51 console.error('Error fetching tweets:', error); 52 return []; // エラー時は空の配列を返す 53 } 54} 55 56module.exports = { 57 data: new SlashCommandBuilder() 58 .setName('search') 59 .setDescription('ツイート取得'), 60 61 async execute(interaction) { 62 try { 63 const tweets = await getTweetsWithHashtag('#(タグ名)'); 64 console.log(tweets); 65 66 if (tweets.length > 0) { 67 // ツイートが取得できた場合の処理 68 const randomTweet = tweets[Math.floor(Math.random() * tweets.length)]; 69 console.log(randomTweet.text); 70 // ツイート情報を埋め込み形式で作成 71 const embed = new EmbedBuilder() 72 .setColor('#FFE201') 73 .setTitle('Random Tweet') 74 .setDescription(randomTweet.text) 75 .addField('Author ID', randomTweet.author_id) 76 .addField('Posted At', new Date(randomTweet.created_at).toUTCString()); 77 78 await interaction.reply({ embeds: [embed] }); 79 } else { 80 // ツイートが取得できなかった場合の処理 81 console.log('No tweets found.'); 82 await interaction.reply('No tweets found.'); 83 } 84 } catch (error) { 85 console.error('Error fetching tweets:', error); 86 await interaction.reply('Error fetching tweets'); 87 } 88 }, 89};
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
もともとteratail等で「ApiResponseError: Request failed with code 401」エラーが発生したため質問し、いただいた回答を元に自分なりに変更したところ403エラーが発生しました。
補足
前に行った質問(APIの設定等)
https://teratail.com/questions/dqc61u6a7o1huq
https://qiita.com/ssmn93/questions/1c08e39769cb499d85a5
https://ja.stackoverflow.com/questions/98939/twitter-api%e3%81%ab%e3%81%8a%e3%81%91%e3%82%8bapiresponseerror-request-failed-with-code-401%e3%82%a8%e3%83%a9%e3%83%bc%e3%81%ae%e8%a7%a3%e6%b6%88/98941#98941
別サイトでの質問
https://qiita.com/ssmn93/questions/8488a912e8b88a4b1e00
https://ja.stackoverflow.com/questions/98944/apiresponseerror-request-failed-with-code-403%e3%82%a8%e3%83%a9%e3%83%bc%e3%81%ae%e8%a7%a3%e6%b6%88
あなたの回答
tips
プレビュー