diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 80d4aa1ed..000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,30 +0,0 @@ -env: - es6: true - node: true -extends: - - "eslint:recommended" - - "plugin:@typescript-eslint/eslint-recommended" - - "plugin:@typescript-eslint/recommended" -globals: - Atomics: readonly - SharedArrayBuffer: readonly -parser: "@typescript-eslint/parser" -parserOptions: - ecmaVersion: 2022 - sourceType: module -plugins: - - "@typescript-eslint" -rules: - indent: - - error - - 4 - linebreak-style: - - error - - unix - quotes: - - error - - double - semi: - - error - - always - "@typescript-eslint/no-namespace": off diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index f7d3c3ce8..e402295f5 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - node-version: [ 18.x ] + node-version: [ 20.x ] steps: - name: Checkout diff --git a/commands.json b/commands.json index d26ad2fbf..f9356777d 100644 --- a/commands.json +++ b/commands.json @@ -1 +1 @@ -[{"type":1,"name":"about","description":"Displays some basic information to help you get started with Bastion.","options":[]},{"type":1,"name":"calculate","description":"Evaluates the specified mathematical expression.","options":[{"type":3,"name":"expression","description":"The expression you want to evaluate.","required":true}]},{"type":1,"name":"changes","description":"See the changes introduced in the current version of Bastion.","options":[]},{"type":1,"name":"channel","description":"Command Group - channel","options":[{"type":1,"name":"create","description":"Create a new channel in the server.","options":[{"type":3,"name":"name","description":"The name of the new channel.","required":true},{"type":4,"name":"type","description":"The type of the new channel.","choices":[{"name":"Text","value":0},{"name":"Voice","value":2},{"name":"Announcement","value":5},{"name":"Stage","value":13},{"name":"Category","value":4}],"required":true},{"type":3,"name":"topic","description":"The topic for the new channel."},{"type":4,"name":"limit","description":"Limit the number of users for the new (voice) channel.","min_value":1,"max_value":99},{"type":4,"name":"slowmode","description":"Enable slowmode with the specified interval (in seconds).","min_value":0,"max_value":21600},{"type":5,"name":"nsfw","description":"Should the channel be NSFW."},{"type":3,"name":"reason","description":"The reason for creating the channel."}]},{"type":1,"name":"delete","description":"Delete the current (or the specified) channel.","options":[{"type":7,"name":"channel","description":"The channel you want to delete."},{"type":3,"name":"reason","description":"The reason for deleting the channel."}]},{"type":1,"name":"info","description":"Displays information on the current (or specified) channel.","options":[{"type":7,"name":"channel","description":"The channel whose information you want to display."}]},{"type":1,"name":"update","description":"Update the specified channel in the server.","options":[{"type":7,"name":"channel","description":"The channel you want to update."},{"type":3,"name":"name","description":"The new name for the channel."},{"type":3,"name":"topic","description":"The new topic for the channel."},{"type":4,"name":"limit","description":"The new limit of users for the (voice) channel.","min_value":1,"max_value":99},{"type":4,"name":"slowmode","description":"The new slowmode interval (in seconds).","min_value":0,"max_value":21600},{"type":5,"name":"nsfw","description":"Should the channel be NSFW."},{"type":3,"name":"reason","description":"The reason for updating the channel."}]}]},{"type":1,"name":"chat","description":"Ask questions or chat with ChatGPT from OpenAI.","options":[{"type":3,"name":"message","description":"Your message.","required":true}]},{"type":1,"name":"claim","description":"Claim any rewards available to you.","options":[]},{"type":1,"name":"comic","description":"Command Group - comic","options":[{"type":1,"name":"garfield","description":"Check the latest Garfield comic.","options":[]},{"type":1,"name":"phd","description":"Check the latest PHD comic, or the specified issue.","options":[{"type":4,"name":"issue","description":"Issue number to see the comic.","min_value":1}]},{"type":1,"name":"xkcd","description":"Check the latest xkcd comic, or the specified issue.","options":[{"type":4,"name":"issue","description":"Issue number to see the comic.","min_value":1}]}]},{"type":1,"name":"config","description":"Command Group - config","options":[{"type":1,"name":"auto-roles","description":"Configure roles that will be auto assigned to members when they join the server.","options":[{"type":8,"name":"role","description":"The role you want to add or remove as an auto role."},{"type":5,"name":"bots","description":"Whether this role should be auto assigned to bots."}]},{"type":1,"name":"auto-threads","description":"Configure auto threads in the server.","options":[]},{"type":1,"name":"farewell","description":"Configure farewell messages in the server.","options":[{"type":7,"name":"channel","description":"The channel where the farewell messages will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom farewell message."},{"type":4,"name":"timeout","description":"The interval after which the farewell message will be deleted.","min_value":1,"max_value":30}]},{"type":2,"name":"filter","description":"Subcommand Group - filter","options":[{"type":1,"name":"emails","description":"Configure Email Filter AutoMod rule in the server.","options":[]},{"type":1,"name":"invites","description":"Configure Invite Filter AutoMod rule in the server.","options":[]},{"type":1,"name":"links","description":"Configure Link Filter AutoMod rule in the server.","options":[]}]},{"type":1,"name":"gambling","description":"Configure gambling in the server.","options":[{"type":10,"name":"multiplier","description":"The reward multiplier."}]},{"type":1,"name":"gamification","description":"Configure gamification in the server.","options":[{"type":5,"name":"messages","description":"Should it show the level up messages."},{"type":7,"name":"channel","description":"The channel where the level up messages will be sent.","channel_types":[0]},{"type":10,"name":"multiplier","description":"The reward multiplier."}]},{"type":1,"name":"greeting","description":"Configure greeting messages in the server.","options":[{"type":7,"name":"channel","description":"The channel where the greeting messages will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom greeting message."},{"type":4,"name":"timeout","description":"The interval after which the greeting message will be deleted.","min_value":1,"max_value":30}]},{"type":1,"name":"live-streams","description":"Follow streamers and get notified in the specified channel when they go live.","options":[{"type":3,"name":"twitch","description":"The twitch channel you want to follow."},{"type":7,"name":"channel","description":"The channel where the notifications will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom message for notification."}]},{"type":2,"name":"logs","description":"Subcommand Group - logs","options":[{"type":1,"name":"content","description":"Configure whether deleted and edited message content should be shown in server logs.","options":[]},{"type":1,"name":"mod","description":"Configure channel for logging moderation events.","options":[{"type":7,"name":"channel","description":"The channel where moderation events will be logged.","channel_types":[0]}]},{"type":1,"name":"server","description":"Configure channel for logging server events.","options":[{"type":7,"name":"channel","description":"The channel where server events will be logged.","channel_types":[0]}]}]},{"type":1,"name":"music","description":"Configure music in the server.","options":[]},{"type":1,"name":"reports","description":"Configure user reports in the server.","options":[{"type":7,"name":"channel","description":"The channel where user reports will be sent.","channel_types":[0]}]},{"type":2,"name":"select-roles","description":"Subcommand Group - select-roles","options":[{"type":1,"name":"add","description":"Create a new Select Role Group.","options":[{"type":3,"name":"message","description":"The content of the Select Role Message for users.","required":true},{"type":7,"name":"channel","description":"The channel where you want to send the Select Role Message.","channel_types":[5,0,2]},{"type":4,"name":"type","description":"The behavior of the Select Role Group.","choices":[{"name":"Add Only","value":1},{"name":"Remove Only","value":2}]},{"type":4,"name":"ui","description":"The variant of Select Role UI.","choices":[{"name":"Buttons","value":0},{"name":"Select Menu","value":1}]},{"type":4,"name":"min","description":"The minimum number of roles users are allowed to select.","min_value":0,"max_value":25},{"type":4,"name":"max","description":"The maximum number of roles users are allowed to select.","min_value":1,"max_value":25}]},{"type":1,"name":"list","description":"List all the Select Role Groups.","options":[{"type":7,"name":"channel","description":"List Select Role Groups only from this channel.","channel_types":[5,0,2]}]},{"type":1,"name":"remove","description":"Remove the specified Select Roles Group.","options":[{"type":3,"name":"id","description":"The Select Roles Group ID.","required":true}]},{"type":1,"name":"update","description":"Update the roles and message for the Select Roles Group.","options":[{"type":3,"name":"id","description":"The Select Roles Group ID.","required":true},{"type":3,"name":"message","description":"The new message content."}]}]},{"type":1,"name":"self-roles","description":"Configure roles that users can assign to themselves.","options":[{"type":8,"name":"role","description":"The role you want to add or remove as a self role."}]},{"type":1,"name":"starboard","description":"Configure starboard in the server.","options":[{"type":7,"name":"channel","description":"The channel where starred messages will be logged.","channel_types":[0]},{"type":4,"name":"threshold","description":"The minimum number of stars a message needs.","min_value":2}]},{"type":1,"name":"streamer-role","description":"Set the role someone is assigned in the server when they start streaming.","options":[{"type":8,"name":"role","description":"The role that should be assigned to users."}]},{"type":1,"name":"suggestions","description":"Configure suggestions in the server.","options":[{"type":7,"name":"channel","description":"The channel where suggestions will be sent.","channel_types":[0]}]},{"type":1,"name":"triggers","description":"Configure triggers and responses in the server.","options":[{"type":3,"name":"add","description":"The pattern that will trigger the response."},{"type":3,"name":"remove","description":"The trigger that you want to remove."},{"type":3,"name":"message","description":"The message response for the trigger."},{"type":3,"name":"emoji","description":"The reaction emoji response for the trigger."}]},{"type":1,"name":"verification","description":"Configure verification in the server.","options":[{"type":8,"name":"role","description":"The role users are assigned when are verified."},{"type":3,"name":"text","description":"Type a text message that will be shown to the users trying to verify."}]},{"type":1,"name":"voice-sessions","description":"Configure voice sessions in the server.","options":[{"type":3,"name":"create","description":"Name of the new voice session category."}]},{"type":1,"name":"voting-channels","description":"Configure voting channels in the server.","options":[{"type":7,"name":"add","description":"The channel you want to add as a voting channel.","channel_types":[0]},{"type":7,"name":"remove","description":"The channel you want to remove as a voting channel.","channel_types":[0]}]}]},{"type":1,"name":"donate","description":"See the ways you can contribute to support the Bastion bot project.","options":[]},{"type":1,"name":"emoji","description":"Command Group - emoji","options":[{"type":1,"name":"info","description":"Displays information on the specified custom emoji.","options":[{"type":3,"name":"emoji","description":"The emoji you want to display.","required":true}]}]},{"type":1,"name":"games","description":"Command Group - games","options":[{"type":1,"name":"8ball","description":"Ask any question to the magic 8 ball and get answers.","options":[{"type":3,"name":"question","description":"The question you want to ask the magic 8 ball.","required":true}]},{"type":1,"name":"flip","description":"Flip coins and see the result.","options":[{"type":4,"name":"coins","description":"The number of coins to flip.","min_value":1,"max_value":128}]},{"type":1,"name":"roll","description":"Roll dice and see the result. Supports dice notation.","options":[{"type":3,"name":"notation","description":"The dice notation."}]},{"type":1,"name":"rps","description":"Play rock paper scissor with Bastion.","options":[{"type":3,"name":"choice","description":"Your choice.","choices":[{"name":"Rock","value":"ROCK"},{"name":"Paper","value":"PAPER"},{"name":"Scissor","value":"SCISSOR"}],"required":true}]},{"type":1,"name":"russian-roulette","description":"Play a game of Russian roulette.","options":[{"type":4,"name":"rounds","description":"The number rounds you want to play.","min_value":1,"max_value":6}]}]},{"type":1,"name":"game-server","description":"Fetch information from nearly any game server that makes its status publicly available.","options":[{"type":3,"name":"game","description":"The game ID for the game server.","required":true},{"type":3,"name":"hostname","description":"The IP address or domain name of the game server.","required":true},{"type":4,"name":"port","description":"The connection port number of the game server. Use the query port if connection port doesn't work.","min_value":1,"max_value":65535}]},{"type":1,"name":"gamestats","description":"Command Group - gamestats","options":[{"type":1,"name":"aimlab","description":"Check stats of any Aim Lab player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true}]},{"type":1,"name":"apex","description":"Check stats of any Apex Legends player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"origin"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"}],"required":true}]},{"type":1,"name":"csgo","description":"Check stats of any Counter-Strike: Global Offensive player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true}]},{"type":1,"name":"fortnite","description":"Check stats of any Fortnite player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"kbm"},{"name":"Console","value":"gamepad"},{"name":"Mobile","value":"touch"}],"required":true}]},{"type":1,"name":"overwatch","description":"Check stats of any Overwatch 2 player.","options":[{"type":3,"name":"username","description":"The BattleTag or username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"pc"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"},{"name":"Nintendo Switch","value":"nintendo-switch"}]},{"type":3,"name":"region","description":"The region of the player.","choices":[{"name":"Americas","value":"us"},{"name":"Europe","value":"eu"},{"name":"Asia","value":"asia"}]}]},{"type":1,"name":"rainbow6","description":"Check stats of any Rainbow 6 player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"uplay"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"}],"required":true}]},{"type":1,"name":"valorant","description":"Check stats of any Valorant player.","options":[{"type":3,"name":"username","description":"The name tag of the player.","required":true},{"type":3,"name":"region","description":"The region of the account.","choices":[{"name":"Americas","value":"na"},{"name":"Europe","value":"eu"},{"name":"Asia-Pacific","value":"ap"},{"name":"Korea","value":"kr"}],"required":true}]}]},{"type":1,"name":"give","description":"Give Bastion Coins and Experience Points to server members or take it from them.","options":[{"type":6,"name":"user","description":"The user whose coins and XP will be updated.","required":true},{"type":4,"name":"coins","description":"The amount of coins you want to give or take.","required":true},{"type":4,"name":"xp","description":"The amount of XP you want to give or take.","required":true}]},{"type":1,"name":"giveaway","description":"Run giveaways in the server.","options":[{"type":3,"name":"title","description":"The title for the giveaway.","required":true},{"type":3,"name":"description","description":"The description for the giveaway."},{"type":4,"name":"winners","description":"The number of winners for the giveaway.","min_value":1,"max_value":9007199254740991},{"type":4,"name":"timer","description":"Number of hours the giveaway should run.","min_value":1,"max_value":720}]},{"type":1,"name":"iam","description":"Assign a self assignable role to yourself.","options":[{"type":8,"name":"role","description":"The role you want to assign yourself."}]},{"type":1,"name":"image","description":"Command Group - image","options":[{"type":1,"name":"generate","description":"Generate an image with DALL-E from OpenAI.","options":[{"type":3,"name":"prompt","description":"A description of the desired image.","required":true},{"type":3,"name":"size","description":"The size of the generated image.","choices":[{"name":"Square","value":"1024x1024"},{"name":"Portrait","value":"1024x1792"},{"name":"Landscape","value":"1792x1024"}]}]}]},{"type":1,"name":"invite","description":"Generates an instant invite for the server.","options":[{"type":5,"name":"temporary","description":"Kick the members if they aren't assigned a role within 24 hours."}]},{"type":1,"name":"leaderboard","description":"Displays the server's leaderboard. You're ranked based on their level, XP, karma, and Bastion Coins.","options":[]},{"type":1,"name":"lmgtfy","description":"Send a LMGTFY link for the search query that teaches people how to do an internet search.","options":[{"type":3,"name":"query","description":"The search query.","required":true},{"type":3,"name":"site","description":"The search engine to use.","choices":[{"name":"DuckDuckGo","value":"d"},{"name":"Google","value":"g"},{"name":"Bing","value":"b"}]}]},{"type":1,"name":"message","description":"Command Group - message","options":[{"type":1,"name":"clear","description":"Clear recent messages (newer than two weeks) in the channel.","options":[{"type":4,"name":"limit","description":"Limit the numbers of messages that should be deleted.","min_value":1},{"type":5,"name":"bots","description":"Should it delete messages from bots."},{"type":5,"name":"pinned","description":"Should it delete messages that are pinned."},{"type":5,"name":"system","description":"Should it delete system messages."},{"type":6,"name":"user","description":"Only delete messages from this user."}]}]},{"type":1,"name":"music","description":"Command Group - music","options":[{"type":1,"name":"join","description":"Moves you to the voice channel where Bastion is currently connected.","options":[]},{"type":1,"name":"now","description":"Shows the song playing right now.","options":[]},{"type":1,"name":"pause","description":"Pause the music playback in the voice channel.","options":[]},{"type":1,"name":"play","description":"Play a specified song in the server.","options":[{"type":3,"name":"song","description":"The song name or link you want to play.","required":true}]},{"type":1,"name":"queue","description":"Displays the current music queue in the server.","options":[{"type":3,"name":"remove","description":"Remove songs matching the specified text from the music queue."},{"type":5,"name":"clear","description":"Remove all songs from the music queue."}]},{"type":1,"name":"resume","description":"Resume the music playback in the voice channel.","options":[]},{"type":1,"name":"shuffle","description":"Shuffle the current music queue.","options":[]},{"type":1,"name":"skip","description":"Skip the current music track that's being played in the voice channel.","options":[]},{"type":1,"name":"stop","description":"Stop the music playback and disconnect from the voice channel.","options":[]}]},{"type":1,"name":"poll","description":"Run polls in the server.","options":[{"type":3,"name":"question","description":"The question for the poll.","required":true},{"type":3,"name":"option1","description":"The 1st option for the poll's answer.","required":true},{"type":3,"name":"option2","description":"The 2nd option for the poll's answer.","required":true},{"type":3,"name":"option3","description":"The 3rd option for the poll's answer."},{"type":3,"name":"option4","description":"The 4th option for the poll's answer."},{"type":3,"name":"option5","description":"The 5th option for the poll's answer."},{"type":3,"name":"option6","description":"The 6th option for the poll's answer."},{"type":3,"name":"option7","description":"The 7th option for the poll's answer."},{"type":3,"name":"option8","description":"The 8th option for the poll's answer."},{"type":3,"name":"option9","description":"The 9th option for the poll's answer."},{"type":3,"name":"option10","description":"The 10th option for the poll's answer."},{"type":4,"name":"timer","description":"Number of hours the poll should run.","min_value":1,"max_value":720}]},{"type":1,"name":"profile","description":"Displays the Bastion profile of the specified user.","options":[{"type":6,"name":"user","description":"The user whose profile you want to display."}]},{"type":1,"name":"report","description":"Report a server member to the moderators of the server.","options":[{"type":6,"name":"user","description":"The user you want to report.","required":true},{"type":3,"name":"reason","description":"The reason you want to report the user.","required":true}]},{"type":1,"name":"role","description":"Command Group - role","options":[{"type":1,"name":"config","description":"Set the description & emoji of the specified role.","options":[{"type":8,"name":"role","description":"The role which you want to configure.","required":true},{"type":3,"name":"emoji","description":"The emoji you want to set as the role emoji."},{"type":3,"name":"description","description":"A description for the role (max 100 characters)."}]},{"type":1,"name":"create","description":"Create a new role in the server.","options":[{"type":3,"name":"name","description":"The name of the new role.","required":true},{"type":3,"name":"color","description":"The color for the new role (in HEX code)."},{"type":5,"name":"icon","description":"The icon for the new role (Image URL or Emoji)."},{"type":5,"name":"hoist","description":"Should the members be displayed separately from others."},{"type":5,"name":"mentionable","description":"Should the role be mentionable."},{"type":3,"name":"reason","description":"The reason for creating the role."}]},{"type":1,"name":"delete","description":"Delete the specified role.","options":[{"type":8,"name":"role","description":"The role you want to delete.","required":true},{"type":3,"name":"reason","description":"The reason for deleting the role."}]},{"type":1,"name":"info","description":"Displays information on the specified role.","options":[{"type":8,"name":"role","description":"The role whose information you want to display.","required":true}]},{"type":1,"name":"levels","description":"Configure level roles. When members reach a level, they get assigned to roles in the level.","options":[{"type":8,"name":"role","description":"The role that should be assigned to users."},{"type":4,"name":"level","description":"The level at which the role should be assigned.","min_value":0},{"type":4,"name":"remove","description":"The level which you want to remove.","min_value":0}]},{"type":1,"name":"update","description":"Update the specified role in the server.","options":[{"type":8,"name":"role","description":"The role you want to update.","required":true},{"type":3,"name":"name","description":"The new name for the role."},{"type":3,"name":"color","description":"The new color for the role (in HEX code)."},{"type":5,"name":"icon","description":"The new icon for the role (Image URL or Emoji)."},{"type":5,"name":"hoist","description":"Should the members be displayed separately from others."},{"type":5,"name":"mentionable","description":"Should the role be mentionable."},{"type":3,"name":"reason","description":"The reason for updating the role."}]}]},{"type":1,"name":"say","description":"Replies with the message you ask it to say.","options":[{"type":3,"name":"message","description":"The message you want Bastion to say.","required":true}]},{"type":1,"name":"search","description":"Command Group - search","options":[{"type":1,"name":"anime","description":"Searches for information on the specified anime.","options":[{"type":3,"name":"name","description":"The name of the anime.","required":true}]},{"type":1,"name":"apod","description":"Displays the astronomy picture of the day from NASA.","options":[]},{"type":1,"name":"cryptocurrency","description":"Searches for information on the specified crypto currency.","options":[{"type":3,"name":"symbol","description":"The symbol of the crypto currency.","required":true}]},{"type":1,"name":"definitions","description":"Displays the definitions for the specified word.","options":[{"type":3,"name":"word","description":"The word you want to lookup.","required":true}]},{"type":1,"name":"game","description":"Searches for information on the specified game.","options":[{"type":3,"name":"name","description":"The name of the game.","required":true}]},{"type":1,"name":"manga","description":"Searches for information on the specified manga.","options":[{"type":3,"name":"name","description":"The name of the manga.","required":true}]},{"type":1,"name":"movie","description":"Searches for information on the specified movie.","options":[{"type":3,"name":"name","description":"The name of the movie.","required":true}]},{"type":1,"name":"pokemon","description":"Searches for information on the specified pokémon.","options":[{"type":3,"name":"name","description":"The name (or number) of the Pokémon.","required":true}]},{"type":1,"name":"redirects","description":"Follows all the redirects in the specified URL and displays the final URL.","options":[{"type":3,"name":"url","description":"The URL you want to follow.","required":true}]},{"type":1,"name":"tv","description":"Searches for information on the specified TV show.","options":[{"type":3,"name":"name","description":"The name of the TV show.","required":true}]},{"type":1,"name":"urban-dictionary","description":"Searches Urban Dictionary for the specified word.","options":[{"type":3,"name":"word","description":"The word you want to lookup.","required":true}]},{"type":1,"name":"weather","description":"Displays the weather forcast of the specified location.","options":[{"type":3,"name":"location","description":"The location for the weather forcast.","required":true}]},{"type":1,"name":"wikipedia","description":"Searches Wikipedia for the specified query.","options":[{"type":3,"name":"query","description":"The search query.","required":true}]}]},{"type":1,"name":"server","description":"Command Group - server","options":[{"type":1,"name":"info","description":"Displays information on the server.","options":[]},{"type":1,"name":"prune","description":"Kicks the inactive members (without any roles) from the server.","options":[{"type":4,"name":"days","description":"The number of days of inactivity required for kicking.","min_value":1},{"type":8,"name":"role","description":"Inactive members check includes this role."},{"type":3,"name":"reason","description":"The reason for pruning the inactive members."}]}]},{"type":1,"name":"status","description":"Displays Bastion's status. You can also see Discord's status.","options":[{"type":5,"name":"shard","description":"Displays the status of the current shard."}]},{"type":1,"name":"su","description":"Command Group - su","options":[{"type":1,"name":"eval","description":"Evaluate JavaScript code in Bastion's context.","options":[{"type":3,"name":"code","description":"The code you want to execute.","required":true},{"type":5,"name":"public","description":"Display the output to everyone."}]},{"type":1,"name":"exec","description":"Execute terminal commands on the system where Bastion is running.","options":[{"type":3,"name":"command","description":"The command you want to execute.","required":true},{"type":5,"name":"public","description":"Display the output to everyone."}]},{"type":1,"name":"reload","description":"Reload Bastion's settings.","options":[]},{"type":1,"name":"shutdown","description":"Shutdown Bastion directly from Discord.","options":[]}]},{"type":1,"name":"suggest","description":"Send a suggestion to the server staff.","options":[{"type":3,"name":"suggestion","description":"What do you want to suggest?","required":true}]},{"type":1,"name":"thread","description":"Command Group - thread","options":[{"type":1,"name":"close","description":"Close and lock the thread.","options":[]},{"type":1,"name":"name","description":"Change the name of the thread.","options":[{"type":3,"name":"name","description":"The new name for the thread.","required":true}]}]},{"type":1,"name":"translate","description":"Translates the specified text from one language to another.","options":[{"type":3,"name":"text","description":"The text you want to translate.","required":true},{"type":3,"name":"to","description":"The language you want to translate to."},{"type":3,"name":"from","description":"The language you want to translate from."}]},{"type":1,"name":"user","description":"Command Group - user","options":[{"type":1,"name":"avatar","description":"Displays the avatar of the specified user.","options":[{"type":6,"name":"user","description":"The user whose avatar you want to display."}]},{"type":1,"name":"info","description":"Displays information on the specified user.","options":[{"type":6,"name":"user","description":"The user whose information you want to display."}]},{"type":1,"name":"infractions","description":"Configure infraction actions and displays infractions of the specified user.","options":[{"type":4,"name":"timeout","description":"Number of violations after which a user is timed out.","min_value":1},{"type":4,"name":"kick","description":"Number of violations after which a user is kicked.","min_value":1},{"type":4,"name":"ban","description":"Number of violations after which a user is banned.","min_value":1},{"type":6,"name":"user","description":"The user whose infractions you want to display."},{"type":4,"name":"remove","description":"The infraction you want to remove from the user.","min_value":1}]}]},{"type":1,"name":"warn","description":"Warn server members and add infractions to their server profile.","options":[{"type":6,"name":"user","description":"The user you want to warn.","required":true},{"type":3,"name":"reason","description":"The reason for the warning.","required":true}]}] \ No newline at end of file +[{"type":3,"name":"Sentiment","description":""},{"type":3,"name":"Translate","description":""},{"type":1,"name":"about","description":"Displays some basic information to help you get started with Bastion.","options":[]},{"type":1,"name":"calculate","description":"Evaluates the specified mathematical expression.","options":[{"type":3,"name":"expression","description":"The expression you want to evaluate.","required":true}]},{"type":1,"name":"changes","description":"See the changes introduced in the current version of Bastion.","options":[]},{"type":1,"name":"channel","description":"Command Group - channel","options":[{"type":1,"name":"create","description":"Create a new channel in the server.","options":[{"type":3,"name":"name","description":"The name of the new channel.","required":true},{"type":4,"name":"type","description":"The type of the new channel.","choices":[{"name":"Text","value":0},{"name":"Voice","value":2},{"name":"Announcement","value":5},{"name":"Stage","value":13},{"name":"Category","value":4}],"required":true},{"type":3,"name":"topic","description":"The topic for the new channel."},{"type":4,"name":"limit","description":"Limit the number of users for the new (voice) channel.","min_value":1,"max_value":99},{"type":4,"name":"slowmode","description":"Enable slowmode with the specified interval (in seconds).","min_value":0,"max_value":21600},{"type":5,"name":"nsfw","description":"Should the channel be NSFW."},{"type":3,"name":"reason","description":"The reason for creating the channel."}]},{"type":1,"name":"delete","description":"Delete the current (or the specified) channel.","options":[{"type":7,"name":"channel","description":"The channel you want to delete."},{"type":3,"name":"reason","description":"The reason for deleting the channel."}]},{"type":1,"name":"info","description":"Displays information on the current (or specified) channel.","options":[{"type":7,"name":"channel","description":"The channel whose information you want to display."}]},{"type":1,"name":"update","description":"Update the specified channel in the server.","options":[{"type":7,"name":"channel","description":"The channel you want to update."},{"type":3,"name":"name","description":"The new name for the channel."},{"type":3,"name":"topic","description":"The new topic for the channel."},{"type":4,"name":"limit","description":"The new limit of users for the (voice) channel.","min_value":1,"max_value":99},{"type":4,"name":"slowmode","description":"The new slowmode interval (in seconds).","min_value":0,"max_value":21600},{"type":5,"name":"nsfw","description":"Should the channel be NSFW."},{"type":3,"name":"reason","description":"The reason for updating the channel."}]}]},{"type":1,"name":"chat","description":"Ask questions or chat with Bastion.","options":[{"type":3,"name":"message","description":"Your message.","required":true}]},{"type":1,"name":"claim","description":"Claim any rewards available to you.","options":[]},{"type":1,"name":"comic","description":"Command Group - comic","options":[{"type":1,"name":"garfield","description":"Check the latest Garfield comic.","options":[]},{"type":1,"name":"phd","description":"Check the latest PHD comic, or the specified issue.","options":[{"type":4,"name":"issue","description":"Issue number to see the comic.","min_value":1}]},{"type":1,"name":"xkcd","description":"Check the latest xkcd comic, or the specified issue.","options":[{"type":4,"name":"issue","description":"Issue number to see the comic.","min_value":1}]}]},{"type":1,"name":"config","description":"Command Group - config","options":[{"type":1,"name":"auto-roles","description":"Configure roles that will be auto assigned to members when they join the server.","options":[{"type":8,"name":"role","description":"The role you want to add or remove as an auto role."},{"type":5,"name":"bots","description":"Whether this role should be auto assigned to bots."}]},{"type":1,"name":"auto-threads","description":"Configure auto threads in the server.","options":[]},{"type":1,"name":"farewell","description":"Configure farewell messages in the server.","options":[{"type":7,"name":"channel","description":"The channel where the farewell messages will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom farewell message."},{"type":4,"name":"timeout","description":"The interval after which the farewell message will be deleted.","min_value":1,"max_value":30}]},{"type":2,"name":"filter","description":"Subcommand Group - filter","options":[{"type":1,"name":"emails","description":"Configure Email Filter AutoMod rule in the server.","options":[]},{"type":1,"name":"invites","description":"Configure Invite Filter AutoMod rule in the server.","options":[]},{"type":1,"name":"links","description":"Configure Link Filter AutoMod rule in the server.","options":[]}]},{"type":1,"name":"gambling","description":"Configure gambling in the server.","options":[{"type":10,"name":"multiplier","description":"The reward multiplier."}]},{"type":1,"name":"gamification","description":"Configure gamification in the server.","options":[{"type":5,"name":"messages","description":"Should it show the level up messages."},{"type":7,"name":"channel","description":"The channel where the level up messages will be sent.","channel_types":[0]},{"type":10,"name":"multiplier","description":"The reward multiplier."}]},{"type":1,"name":"greeting","description":"Configure greeting messages in the server.","options":[{"type":7,"name":"channel","description":"The channel where the greeting messages will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom greeting message."},{"type":4,"name":"timeout","description":"The interval after which the greeting message will be deleted.","min_value":1,"max_value":30}]},{"type":1,"name":"live-streams","description":"Follow streamers and get notified in the specified channel when they go live.","options":[{"type":3,"name":"twitch","description":"The twitch channel you want to follow."},{"type":7,"name":"channel","description":"The channel where the notifications will be sent.","channel_types":[0]},{"type":3,"name":"message","description":"The custom message for notification."}]},{"type":2,"name":"logs","description":"Subcommand Group - logs","options":[{"type":1,"name":"content","description":"Configure whether deleted and edited message content should be shown in server logs.","options":[]},{"type":1,"name":"mod","description":"Configure channel for logging moderation events.","options":[{"type":7,"name":"channel","description":"The channel where moderation events will be logged.","channel_types":[0]}]},{"type":1,"name":"server","description":"Configure channel for logging server events.","options":[{"type":7,"name":"channel","description":"The channel where server events will be logged.","channel_types":[0]}]}]},{"type":1,"name":"music","description":"Configure music in the server.","options":[]},{"type":1,"name":"reports","description":"Configure user reports in the server.","options":[{"type":7,"name":"channel","description":"The channel where user reports will be sent.","channel_types":[0]}]},{"type":2,"name":"select-roles","description":"Subcommand Group - select-roles","options":[{"type":1,"name":"add","description":"Create a new Select Role Group.","options":[{"type":3,"name":"message","description":"The content of the Select Role Message for users.","required":true},{"type":7,"name":"channel","description":"The channel where you want to send the Select Role Message.","channel_types":[5,0,2]},{"type":4,"name":"type","description":"The behavior of the Select Role Group.","choices":[{"name":"Add Only","value":1},{"name":"Remove Only","value":2}]},{"type":4,"name":"ui","description":"The variant of Select Role UI.","choices":[{"name":"Buttons","value":0},{"name":"Select Menu","value":1}]},{"type":4,"name":"min","description":"The minimum number of roles users are allowed to select.","min_value":0,"max_value":25},{"type":4,"name":"max","description":"The maximum number of roles users are allowed to select.","min_value":1,"max_value":25}]},{"type":1,"name":"list","description":"List all the Select Role Groups.","options":[{"type":7,"name":"channel","description":"List Select Role Groups only from this channel.","channel_types":[5,0,2]}]},{"type":1,"name":"remove","description":"Remove the specified Select Roles Group.","options":[{"type":3,"name":"id","description":"The Select Roles Group ID.","required":true}]},{"type":1,"name":"update","description":"Update the roles and message for the Select Roles Group.","options":[{"type":3,"name":"id","description":"The Select Roles Group ID.","required":true},{"type":3,"name":"message","description":"The new message content."},{"type":5,"name":"roles","description":"Reset the roles so you can add them again."}]}]},{"type":1,"name":"self-roles","description":"Configure roles that users can assign to themselves.","options":[{"type":8,"name":"role","description":"The role you want to add or remove as a self role."}]},{"type":1,"name":"starboard","description":"Configure starboard in the server.","options":[{"type":7,"name":"channel","description":"The channel where starred messages will be logged.","channel_types":[0]},{"type":4,"name":"threshold","description":"The minimum number of stars a message needs.","min_value":2}]},{"type":1,"name":"streamer-role","description":"Set the role someone is assigned in the server when they start streaming.","options":[{"type":8,"name":"role","description":"The role that should be assigned to users."}]},{"type":1,"name":"suggestions","description":"Configure suggestions in the server.","options":[{"type":7,"name":"channel","description":"The channel where suggestions will be sent.","channel_types":[0]}]},{"type":1,"name":"triggers","description":"Configure triggers and responses in the server.","options":[{"type":3,"name":"add","description":"The pattern that will trigger the response."},{"type":3,"name":"remove","description":"The trigger that you want to remove."},{"type":3,"name":"message","description":"The message response for the trigger."},{"type":3,"name":"emoji","description":"The reaction emoji response for the trigger."}]},{"type":1,"name":"verification","description":"Configure verification in the server.","options":[{"type":8,"name":"role","description":"The role users are assigned when are verified."},{"type":3,"name":"text","description":"Type a text message that will be shown to the users trying to verify."}]},{"type":1,"name":"voice-sessions","description":"Configure voice sessions in the server.","options":[{"type":3,"name":"create","description":"Name of the new voice session category."}]},{"type":1,"name":"voting-channels","description":"Configure voting channels in the server.","options":[{"type":7,"name":"add","description":"The channel you want to add as a voting channel.","channel_types":[0]},{"type":7,"name":"remove","description":"The channel you want to remove as a voting channel.","channel_types":[0]}]}]},{"type":1,"name":"donate","description":"See the ways you can contribute to support the Bastion bot project.","options":[]},{"type":1,"name":"emoji","description":"Command Group - emoji","options":[{"type":1,"name":"info","description":"Displays information on the specified custom emoji.","options":[{"type":3,"name":"emoji","description":"The emoji you want to display.","required":true}]}]},{"type":1,"name":"games","description":"Command Group - games","options":[{"type":1,"name":"8ball","description":"Ask any question to the magic 8 ball and get answers.","options":[{"type":3,"name":"question","description":"The question you want to ask the magic 8 ball.","required":true}]},{"type":1,"name":"flip","description":"Flip coins and see the result.","options":[{"type":4,"name":"coins","description":"The number of coins to flip.","min_value":1,"max_value":128}]},{"type":1,"name":"roll","description":"Roll dice and see the result. Supports dice notation.","options":[{"type":3,"name":"notation","description":"The dice notation."}]},{"type":1,"name":"rps","description":"Play rock paper scissor with Bastion.","options":[{"type":3,"name":"choice","description":"Your choice.","choices":[{"name":"Rock","value":"ROCK"},{"name":"Paper","value":"PAPER"},{"name":"Scissor","value":"SCISSOR"}],"required":true}]},{"type":1,"name":"russian-roulette","description":"Play a game of Russian roulette.","options":[{"type":4,"name":"rounds","description":"The number rounds you want to play.","min_value":1,"max_value":6}]}]},{"type":1,"name":"game-server","description":"Fetch information from nearly any game server that makes its status publicly available.","options":[{"type":3,"name":"game","description":"The game ID for the game server.","required":true},{"type":3,"name":"hostname","description":"The IP address or domain name of the game server.","required":true},{"type":4,"name":"port","description":"The connection port number of the game server. Use the query port if connection port doesn't work.","min_value":1,"max_value":65535}]},{"type":1,"name":"gamestats","description":"Command Group - gamestats","options":[{"type":1,"name":"aimlab","description":"Check stats of any Aim Lab player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true}]},{"type":1,"name":"apex","description":"Check stats of any Apex Legends player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"origin"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"}],"required":true}]},{"type":1,"name":"csgo","description":"Check stats of any Counter-Strike: Global Offensive player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true}]},{"type":1,"name":"fortnite","description":"Check stats of any Fortnite player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"kbm"},{"name":"Console","value":"gamepad"},{"name":"Mobile","value":"touch"}],"required":true}]},{"type":1,"name":"overwatch","description":"Check stats of any Overwatch 2 player.","options":[{"type":3,"name":"username","description":"The BattleTag or username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"pc"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"},{"name":"Nintendo Switch","value":"nintendo-switch"}]},{"type":3,"name":"region","description":"The region of the player.","choices":[{"name":"Americas","value":"us"},{"name":"Europe","value":"eu"},{"name":"Asia","value":"asia"}]}]},{"type":1,"name":"rainbow6","description":"Check stats of any Rainbow 6 player.","options":[{"type":3,"name":"username","description":"The username of the player.","required":true},{"type":3,"name":"platform","description":"The platform of the player.","choices":[{"name":"PC","value":"uplay"},{"name":"PlayStation","value":"psn"},{"name":"Xbox","value":"xbl"}],"required":true}]},{"type":1,"name":"valorant","description":"Check stats of any Valorant player.","options":[{"type":3,"name":"username","description":"The name tag of the player.","required":true},{"type":3,"name":"region","description":"The region of the account.","choices":[{"name":"Americas","value":"na"},{"name":"Europe","value":"eu"},{"name":"Asia-Pacific","value":"ap"},{"name":"Korea","value":"kr"}],"required":true}]}]},{"type":1,"name":"give","description":"Give Bastion Coins and Experience Points to server members or take it from them.","options":[{"type":6,"name":"user","description":"The user whose coins and XP will be updated.","required":true},{"type":4,"name":"coins","description":"The amount of coins you want to give or take.","required":true},{"type":4,"name":"xp","description":"The amount of XP you want to give or take.","required":true}]},{"type":1,"name":"giveaway","description":"Run giveaways in the server.","options":[{"type":3,"name":"title","description":"The title for the giveaway.","required":true},{"type":3,"name":"description","description":"The description for the giveaway."},{"type":4,"name":"winners","description":"The number of winners for the giveaway.","min_value":1,"max_value":9007199254740991},{"type":4,"name":"timer","description":"Number of hours the giveaway should run.","min_value":1,"max_value":720}]},{"type":1,"name":"iam","description":"Assign a self assignable role to yourself.","options":[{"type":8,"name":"role","description":"The role you want to assign yourself."}]},{"type":1,"name":"image","description":"Command Group - image","options":[{"type":1,"name":"generate","description":"Generate an image with DALL-E from OpenAI.","options":[{"type":3,"name":"prompt","description":"A description of the desired image.","required":true},{"type":3,"name":"size","description":"The size of the generated image.","choices":[{"name":"Square","value":"1024x1024"},{"name":"Portrait","value":"1024x1792"},{"name":"Landscape","value":"1792x1024"}]}]}]},{"type":1,"name":"invite","description":"Generates an instant invite for the server.","options":[{"type":5,"name":"temporary","description":"Kick the members if they aren't assigned a role within 24 hours."}]},{"type":1,"name":"leaderboard","description":"Displays the server's leaderboard. You're ranked based on their level, XP, karma, and Bastion Coins.","options":[]},{"type":1,"name":"lmgtfy","description":"Send a LMGTFY link for the search query that teaches people how to do an internet search.","options":[{"type":3,"name":"query","description":"The search query.","required":true},{"type":3,"name":"site","description":"The search engine to use.","choices":[{"name":"DuckDuckGo","value":"d"},{"name":"Google","value":"g"},{"name":"Bing","value":"b"}]}]},{"type":1,"name":"message","description":"Command Group - message","options":[{"type":1,"name":"clear","description":"Clear recent messages (newer than two weeks) in the channel.","options":[{"type":4,"name":"limit","description":"Limit the numbers of messages that should be deleted.","min_value":1},{"type":5,"name":"bots","description":"Should it delete messages from bots."},{"type":5,"name":"pinned","description":"Should it delete messages that are pinned."},{"type":5,"name":"system","description":"Should it delete system messages."},{"type":6,"name":"user","description":"Only delete messages from this user."}]}]},{"type":1,"name":"music","description":"Command Group - music","options":[{"type":1,"name":"join","description":"Moves you to the voice channel where Bastion is currently connected.","options":[]},{"type":1,"name":"now","description":"Shows the song playing right now.","options":[]},{"type":1,"name":"pause","description":"Pause the music playback in the voice channel.","options":[]},{"type":1,"name":"play","description":"Play a specified song in the server.","options":[{"type":3,"name":"song","description":"The song name or link you want to play.","required":true}]},{"type":1,"name":"queue","description":"Displays the current music queue in the server.","options":[{"type":3,"name":"remove","description":"Remove songs matching the specified text from the music queue."},{"type":5,"name":"clear","description":"Remove all songs from the music queue."}]},{"type":1,"name":"resume","description":"Resume the music playback in the voice channel.","options":[]},{"type":1,"name":"shuffle","description":"Shuffle the current music queue.","options":[]},{"type":1,"name":"skip","description":"Skip the current music track that's being played in the voice channel.","options":[]},{"type":1,"name":"stop","description":"Stop the music playback and disconnect from the voice channel.","options":[]}]},{"type":1,"name":"poll","description":"Run polls in the server.","options":[{"type":3,"name":"question","description":"The question for the poll.","required":true},{"type":3,"name":"option1","description":"The 1st option for the poll's answer.","required":true},{"type":3,"name":"option2","description":"The 2nd option for the poll's answer.","required":true},{"type":3,"name":"option3","description":"The 3rd option for the poll's answer."},{"type":3,"name":"option4","description":"The 4th option for the poll's answer."},{"type":3,"name":"option5","description":"The 5th option for the poll's answer."},{"type":3,"name":"option6","description":"The 6th option for the poll's answer."},{"type":3,"name":"option7","description":"The 7th option for the poll's answer."},{"type":3,"name":"option8","description":"The 8th option for the poll's answer."},{"type":3,"name":"option9","description":"The 9th option for the poll's answer."},{"type":3,"name":"option10","description":"The 10th option for the poll's answer."},{"type":4,"name":"timer","description":"Number of hours the poll should run.","min_value":1,"max_value":720}]},{"type":1,"name":"profile","description":"Displays the Bastion profile of the specified user.","options":[{"type":6,"name":"user","description":"The user whose profile you want to display."}]},{"type":1,"name":"report","description":"Report a server member to the moderators of the server.","options":[{"type":6,"name":"user","description":"The user you want to report.","required":true},{"type":3,"name":"reason","description":"The reason you want to report the user.","required":true}]},{"type":1,"name":"role","description":"Command Group - role","options":[{"type":1,"name":"config","description":"Set the description & emoji of the specified role.","options":[{"type":8,"name":"role","description":"The role which you want to configure.","required":true},{"type":3,"name":"emoji","description":"The emoji you want to set as the role emoji."},{"type":3,"name":"description","description":"A description for the role (max 100 characters)."}]},{"type":1,"name":"create","description":"Create a new role in the server.","options":[{"type":3,"name":"name","description":"The name of the new role.","required":true},{"type":3,"name":"color","description":"The color for the new role (in HEX code)."},{"type":5,"name":"icon","description":"The icon for the new role (Image URL or Emoji)."},{"type":5,"name":"hoist","description":"Should the members be displayed separately from others."},{"type":5,"name":"mentionable","description":"Should the role be mentionable."},{"type":3,"name":"reason","description":"The reason for creating the role."}]},{"type":1,"name":"delete","description":"Delete the specified role.","options":[{"type":8,"name":"role","description":"The role you want to delete.","required":true},{"type":3,"name":"reason","description":"The reason for deleting the role."}]},{"type":1,"name":"info","description":"Displays information on the specified role.","options":[{"type":8,"name":"role","description":"The role whose information you want to display.","required":true}]},{"type":1,"name":"levels","description":"Configure level roles. When members reach a level, they get assigned to roles in the level.","options":[{"type":8,"name":"role","description":"The role that should be assigned to users."},{"type":4,"name":"level","description":"The level at which the role should be assigned.","min_value":0},{"type":4,"name":"remove","description":"The level which you want to remove.","min_value":0}]},{"type":1,"name":"update","description":"Update the specified role in the server.","options":[{"type":8,"name":"role","description":"The role you want to update.","required":true},{"type":3,"name":"name","description":"The new name for the role."},{"type":3,"name":"color","description":"The new color for the role (in HEX code)."},{"type":5,"name":"icon","description":"The new icon for the role (Image URL or Emoji)."},{"type":5,"name":"hoist","description":"Should the members be displayed separately from others."},{"type":5,"name":"mentionable","description":"Should the role be mentionable."},{"type":3,"name":"reason","description":"The reason for updating the role."}]}]},{"type":1,"name":"say","description":"Replies with the message you ask it to say.","options":[{"type":3,"name":"message","description":"The message you want Bastion to say.","required":true}]},{"type":1,"name":"search","description":"Command Group - search","options":[{"type":1,"name":"anime","description":"Searches for information on the specified anime.","options":[{"type":3,"name":"name","description":"The name of the anime.","required":true}]},{"type":1,"name":"apod","description":"Displays the astronomy picture of the day from NASA.","options":[]},{"type":1,"name":"cryptocurrency","description":"Searches for information on the specified crypto currency.","options":[{"type":3,"name":"symbol","description":"The symbol of the crypto currency.","required":true}]},{"type":1,"name":"definitions","description":"Displays the definitions for the specified word.","options":[{"type":3,"name":"word","description":"The word you want to lookup.","required":true}]},{"type":1,"name":"game","description":"Searches for information on the specified game.","options":[{"type":3,"name":"name","description":"The name of the game.","required":true}]},{"type":1,"name":"manga","description":"Searches for information on the specified manga.","options":[{"type":3,"name":"name","description":"The name of the manga.","required":true}]},{"type":1,"name":"movie","description":"Searches for information on the specified movie.","options":[{"type":3,"name":"name","description":"The name of the movie.","required":true}]},{"type":1,"name":"pokemon","description":"Searches for information on the specified pokémon.","options":[{"type":3,"name":"name","description":"The name (or number) of the Pokémon.","required":true}]},{"type":1,"name":"redirects","description":"Follows all the redirects in the specified URL and displays the final URL.","options":[{"type":3,"name":"url","description":"The URL you want to follow.","required":true}]},{"type":1,"name":"tv","description":"Searches for information on the specified TV show.","options":[{"type":3,"name":"name","description":"The name of the TV show.","required":true}]},{"type":1,"name":"urban-dictionary","description":"Searches Urban Dictionary for the specified word.","options":[{"type":3,"name":"word","description":"The word you want to lookup.","required":true}]},{"type":1,"name":"weather","description":"Displays the weather forcast of the specified location.","options":[{"type":3,"name":"location","description":"The location for the weather forcast.","required":true}]},{"type":1,"name":"wikipedia","description":"Searches Wikipedia for the specified query.","options":[{"type":3,"name":"query","description":"The search query.","required":true}]}]},{"type":1,"name":"server","description":"Command Group - server","options":[{"type":1,"name":"info","description":"Displays information on the server.","options":[]},{"type":1,"name":"prune","description":"Kicks the inactive members (without any roles) from the server.","options":[{"type":4,"name":"days","description":"The number of days of inactivity required for kicking.","min_value":1},{"type":8,"name":"role","description":"Inactive members check includes this role."},{"type":3,"name":"reason","description":"The reason for pruning the inactive members."}]}]},{"type":1,"name":"status","description":"Displays Bastion's status. You can also see Discord's status.","options":[{"type":5,"name":"shard","description":"Displays the status of the current shard."}]},{"type":1,"name":"su","description":"Command Group - su","options":[{"type":1,"name":"eval","description":"Evaluate JavaScript code in Bastion's context.","options":[{"type":3,"name":"code","description":"The code you want to execute.","required":true},{"type":5,"name":"public","description":"Display the output to everyone."}]},{"type":1,"name":"exec","description":"Execute terminal commands on the system where Bastion is running.","options":[{"type":3,"name":"command","description":"The command you want to execute.","required":true},{"type":5,"name":"public","description":"Display the output to everyone."}]},{"type":1,"name":"reload","description":"Reload Bastion's settings.","options":[]},{"type":1,"name":"shutdown","description":"Shutdown Bastion directly from Discord.","options":[]}]},{"type":1,"name":"suggest","description":"Send a suggestion to the server staff.","options":[{"type":3,"name":"suggestion","description":"What do you want to suggest?","required":true}]},{"type":1,"name":"thread","description":"Command Group - thread","options":[{"type":1,"name":"close","description":"Close and lock the thread.","options":[]},{"type":1,"name":"name","description":"Change the name of the thread.","options":[{"type":3,"name":"name","description":"The new name for the thread.","required":true}]}]},{"type":1,"name":"translate","description":"Translates the specified text from one language to another.","options":[{"type":3,"name":"text","description":"The text you want to translate.","required":true},{"type":3,"name":"to","description":"The language you want to translate to."},{"type":3,"name":"from","description":"The language you want to translate from."}]},{"type":1,"name":"user","description":"Command Group - user","options":[{"type":1,"name":"avatar","description":"Displays the avatar of the specified user.","options":[{"type":6,"name":"user","description":"The user whose avatar you want to display."}]},{"type":1,"name":"info","description":"Displays information on the specified user.","options":[{"type":6,"name":"user","description":"The user whose information you want to display."}]},{"type":1,"name":"infractions","description":"Configure infraction actions and displays infractions of the specified user.","options":[{"type":4,"name":"timeout","description":"Number of violations after which a user is timed out.","min_value":1},{"type":4,"name":"kick","description":"Number of violations after which a user is kicked.","min_value":1},{"type":4,"name":"ban","description":"Number of violations after which a user is banned.","min_value":1},{"type":6,"name":"user","description":"The user whose infractions you want to display."},{"type":4,"name":"remove","description":"The infraction you want to remove from the user.","min_value":1}]}]},{"type":1,"name":"warn","description":"Warn server members and add infractions to their server profile.","options":[{"type":6,"name":"user","description":"The user you want to warn.","required":true},{"type":3,"name":"reason","description":"The reason for the warning.","required":true}]}] \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..921abe915 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,25 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config({ + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + sourceType: "module", + }, + files: [ "src/**/*.ts" ], + rules: { + indent: [ "warn", 4 ], + "linebreak-style": [ "warn", "unix" ], + quotes: [ "warn", "double" ], + semi: [ "warn", "always" ], + "@typescript-eslint/no-namespace": "off", + }, +}); diff --git a/package.json b/package.json index 02accf2bc..aed7fdb43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bastion", - "version": "10.15.1", + "version": "10.16.0", "description": "Get an enhanced Discord experience!", "type": "module", "homepage": "https://bastion.traction.one", @@ -15,35 +15,36 @@ "commands": "tesseract publish", "migrate": "node ./dist/migrate.js", "start": "node .", - "test": "eslint src --ext .ts" + "test": "eslint ." }, "devDependencies": { "@types/discord-rpc": "^4.0.8", "@types/express": "^4.17.21", - "@types/gamedig": "^5.0.2", + "@types/gamedig": "^5.0.3", "@types/http-errors": "^2.0.4", - "@types/jsdom": "^21.1.6", - "@types/node": "^20.12.12", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", - "eslint": "^8.57.0", - "typescript": "^5.4.5" + "@types/jsdom": "^21.1.7", + "@types/node": "^22.7.4", + "eslint": "^9.12.0", + "typescript": "^5.6.2", + "typescript-eslint": "^8.8.0" }, "dependencies": { - "@bastion/tesseract": "^5.2.0", + "@anthropic-ai/sdk": "^0.28.0", + "@bastion/tesseract": "^5.2.2", "@discordjs/opus": "^0.9.0", + "@google/generative-ai": "^0.21.0", "@iamtraction/google-translate": "^2.0.1", "@iamtraction/play-dl": "^1.9.8", "discord-rpc": "^4.0.1", - "dotenv": "^16.3.1", - "emoji-regex": "^10.3.0", - "gamedig": "^5.0.0", - "jsdom": "^24.0.0", - "libsodium-wrappers": "^0.7.13", - "mathjs": "^13.0.0", - "openai": "^4.47.1", + "dotenv": "^16.4.5", + "emoji-regex": "^10.4.0", + "gamedig": "^5.1.3", + "jsdom": "^25.0.1", + "libsodium-wrappers": "^0.7.15", + "mathjs": "^13.2.0", + "openai": "^4.67.1", "r6api.js": "^4.4.1", - "undici": "^6.18.0", + "undici": "^6.19.8", "ytdl-core": "^4.11.5", "ytpl": "^2.3.0" }, diff --git a/settings.example.yaml b/settings.example.yaml index adac2bef7..4910f6c7d 100644 --- a/settings.example.yaml +++ b/settings.example.yaml @@ -84,11 +84,34 @@ nasaApiKey: "DEMO_KEY" # For more details, check https://openai.com/pricing openai: apiKey: "" - # If you want to use GPT-4, set `model` to `gpt-4`. - model: "gpt-3.5-turbo" + # If you want to use GPT-4o, set `model` to `gpt-4o`. + # https://openai.com/api/pricing + model: "gpt-4o-mini" # Change the `maxTokens` value to set the length of ChatGPT's responses. # https://platform.openai.com/tokenizer maxTokens: 100 +# Required for `chat` command to use the Google's Gemini APIs. +# API pricing depends on these values. +# For more details, check https://ai.google.dev/pricing +gemini: + apiKey: "" + # If you want to use Gemini 1.5 Pro, set `model` to `gemini-1.5-pro`. + # https://ai.google.dev/gemini-api/docs/models/gemini + model: "gemini-1.5-flash" + # Change the `maxOutputTokens` value to set the length of ChatGPT's responses. + # https://ai.google.dev/gemini-api/docs/tokens + maxOutputTokens: 256 +# Required for `chat` command to use the Anthropic's APIs. +# API pricing depends on these values. +# For more details, check https://www.anthropic.com/pricing#anthropic-api +anthropic: + apiKey: "" + # If you want to use Claude 3.5 Sonnet, set `model` to `claude-3-5-sonnet-20240620`. + # https://docs.anthropic.com/en/docs/about-claude/models + model: "claude-3-haiku-20240307" + # Change the `maxTokens` value to set the length of Claude's responses. + # https://docs.anthropic.com/en/docs/about-claude/models#model-comparison-table + maxTokens: 128 # Required for `weather` command. openWeatherMapApiKey: "" # Required for `movie` and `tv` commands. diff --git a/src/actions/Sentiment.ts b/src/actions/Sentiment.ts new file mode 100644 index 000000000..822c61ecd --- /dev/null +++ b/src/actions/Sentiment.ts @@ -0,0 +1,110 @@ +/*! + * @author TRACTION (iamtraction) + * @copyright 2024 + */ +import { ApplicationCommandType, MessageContextMenuCommandInteraction } from "discord.js"; +import { Client, Command } from "@bastion/tesseract"; +import Anthropic from "@anthropic-ai/sdk"; +import OpenAI from "openai"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +import Settings from "../utils/settings.js"; + +class SentimentCommand extends Command { + constructor() { + super({ + type: ApplicationCommandType.Message, + name: "Sentiment", + description: "", + owner: true, + }); + } + + public async exec(interaction: MessageContextMenuCommandInteraction<"cached">): Promise { + if (!interaction.targetMessage.content) return; + + const systemPrompt = "You are a helpful and informative AI assistant. You will analyze the given text and provide an assessment of its sentiment. Consider the overall tone, specific words and phrases used, and any contextual clues. Your response should be short, concise, objective, and informative."; + const sentimentPrompt = `Please analyze the following text and provide a assessment of its sentiment: ${ interaction.targetMessage.content }`; + + // use ChatGPT if OpenAI API key is present + if (((interaction.client as Client).settings as Settings).get("openai").apiKey) { + const openai = new OpenAI({ + apiKey: ((interaction.client as Client).settings as Settings).get("openai").apiKey, + }); + + const response = await openai.chat.completions.create({ + model: ((interaction.client as Client).settings as Settings).get("openai").model, + messages: [ + { + role: "system", + content: systemPrompt, + }, + { + role: "user", + content: sentimentPrompt, + }, + ], + max_tokens: ((interaction.client as Client).settings as Settings).get("openai").maxTokens, + user: interaction.member.id, + }); + + return await interaction.reply({ + content: response.choices[0].message.content, + ephemeral: true, + }); + } + + // use Gemini if Gemini API key is present + if (((interaction.client as Client).settings as Settings).get("gemini").apiKey) { + const gemini = new GoogleGenerativeAI(((interaction.client as Client).settings as Settings).get("gemini").apiKey); + const geminiModel = gemini.getGenerativeModel({ + model: ((interaction.client as Client).settings as Settings).get("gemini").model, + systemInstruction: systemPrompt, + generationConfig: { + maxOutputTokens: ((interaction.client as Client).settings as Settings).get("gemini").maxOutputTokens, + }, + }); + + const result = await geminiModel.generateContent(sentimentPrompt); + + return await interaction.reply({ + content: result.response.text(), + ephemeral: true, + }); + } + + // use Claude if Anthropic API key is present + if (((interaction.client as Client).settings as Settings).get("anthropic").apiKey) { + const anthropic = new Anthropic({ + apiKey: ((interaction.client as Client).settings as Settings).get("anthropic").apiKey, + }); + + const result = await anthropic.messages.create({ + model: ((interaction.client as Client).settings as Settings).get("anthropic").model, + system: systemPrompt, + max_tokens: ((interaction.client as Client).settings as Settings).get("anthropic").maxTokens, + messages: [ + { + role: "user", + content: [ + { + type: "text", + text: sentimentPrompt, + }, + ], + }, + ], + metadata: { + user_id: interaction.member.id, + }, + }); + + return await interaction.reply({ + content: result.content[0].type === "text" && result.content[0].text, + ephemeral: true, + }); + } + } +} + +export { SentimentCommand as Command }; diff --git a/src/actions/Translate.ts b/src/actions/Translate.ts new file mode 100644 index 000000000..b290a83e0 --- /dev/null +++ b/src/actions/Translate.ts @@ -0,0 +1,32 @@ +/*! + * @author TRACTION (iamtraction) + * @copyright 2024 + */ +import { ApplicationCommandType, MessageContextMenuCommandInteraction } from "discord.js"; +import { Command } from "@bastion/tesseract"; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import translate = require("@iamtraction/google-translate"); + +class TranslateCommand extends Command { + constructor() { + super({ + type: ApplicationCommandType.Message, + name: "Translate", + description: "", + }); + } + + public async exec(interaction: MessageContextMenuCommandInteraction<"cached">): Promise { + if (!interaction.targetMessage.content) return; + + // fetch the translation + const response = await translate(interaction.targetMessage.content, { to: "en" }); + + return await interaction.reply({ + content: `${ response.text } +-# Translated from ${ response.from.language.iso?.toUpperCase() } to English`, + }); + } +} + +export { TranslateCommand as Command }; diff --git a/src/commands/chat.ts b/src/commands/chat.ts index 8fd4d7bd3..93cd987fa 100644 --- a/src/commands/chat.ts +++ b/src/commands/chat.ts @@ -4,7 +4,9 @@ */ import { ApplicationCommandOptionType, ChatInputCommandInteraction } from "discord.js"; import { Client, Command } from "@bastion/tesseract"; +import Anthropic from "@anthropic-ai/sdk"; import OpenAI from "openai"; +import { GoogleGenerativeAI } from "@google/generative-ai"; import Settings from "../utils/settings.js"; @@ -12,7 +14,7 @@ class ChatCommand extends Command { constructor() { super({ name: "chat", - description: "Ask questions or chat with ChatGPT from OpenAI.", + description: "Ask questions or chat with Bastion.", owner: true, options: [ { @@ -25,29 +27,83 @@ class ChatCommand extends Command { }); } - public async exec(interaction: ChatInputCommandInteraction<"cached">): Promise { + public async exec(interaction: ChatInputCommandInteraction<"cached">): Promise { await interaction.deferReply(); const message = interaction.options.getString("message"); - const openai = new OpenAI({ - apiKey: ((interaction.client as Client).settings as Settings).get("openai").apiKey, - }); + // use ChatGPT if OpenAI API key is present + if (((interaction.client as Client).settings as Settings).get("openai").apiKey) { + const openai = new OpenAI({ + apiKey: ((interaction.client as Client).settings as Settings).get("openai").apiKey, + }); - const response = await openai.chat.completions.create({ - model: ((interaction.client as Client).settings as Settings).get("openai").model || "gpt-3.5-turbo", - messages: [ - { - role: "user", - content: message, + const response = await openai.chat.completions.create({ + model: ((interaction.client as Client).settings as Settings).get("openai").model, + messages: [ + { + role: "user", + content: message, + }, + ], + max_tokens: ((interaction.client as Client).settings as Settings).get("openai").maxTokens || 100, + user: interaction.member.id, + }); + + return await interaction.editReply({ + content: response.choices[0].message.content, + }); + } + + // use Gemini if Gemini API key is present + if (((interaction.client as Client).settings as Settings).get("gemini").apiKey) { + const gemini = new GoogleGenerativeAI(((interaction.client as Client).settings as Settings).get("gemini").apiKey); + const geminiModel = gemini.getGenerativeModel({ + model: ((interaction.client as Client).settings as Settings).get("gemini").model, + generationConfig: { + maxOutputTokens: ((interaction.client as Client).settings as Settings).get("gemini").maxOutputTokens, }, - ], - max_tokens: ((interaction.client as Client).settings as Settings).get("openai").maxTokens || 100, - user: interaction.member.id, - }); + }); + + const result = await geminiModel.generateContent(message); + + return await interaction.editReply({ + content: result.response.text(), + }); + } + + // use Claude if Anthropic API key is present + if (((interaction.client as Client).settings as Settings).get("anthropic").apiKey) { + const anthropic = new Anthropic({ + apiKey: ((interaction.client as Client).settings as Settings).get("anthropic").apiKey, + }); + + const result = await anthropic.messages.create({ + model: ((interaction.client as Client).settings as Settings).get("anthropic").model, + max_tokens: ((interaction.client as Client).settings as Settings).get("anthropic").maxTokens, + messages: [ + { + role: "user", + content: [ + { + type: "text", + text: message, + }, + ], + }, + ], + metadata: { + user_id: interaction.member.id, + }, + }); + + return await interaction.editReply({ + content: result.content[0].type === "text" && result.content[0].text, + }); + } - await interaction.editReply({ - content: response.choices[0].message.content, + return await interaction.editReply({ + content: "You haven't set up API keys for any gen AI APIs.", }); } } diff --git a/src/commands/config/select-roles/update.ts b/src/commands/config/select-roles/update.ts index 548b45715..ac262dc56 100644 --- a/src/commands/config/select-roles/update.ts +++ b/src/commands/config/select-roles/update.ts @@ -29,6 +29,11 @@ class SelectRolesUpdateCommand extends Command { name: "message", description: "The new message content.", }, + { + type: ApplicationCommandOptionType.Boolean, + name: "roles", + description: "Reset the roles so you can add them again.", + }, ], userPermissions: [ PermissionFlagsBits.ManageGuild ], }); @@ -38,6 +43,7 @@ class SelectRolesUpdateCommand extends Command { await interaction.deferReply(); const id = interaction.options.getString("id"); const message = generateEmbed(interaction.options.getString("message") || ""); + const resetRoles = interaction.options.getBoolean("roles"); // get the select roles group document const selectRoleGroup = await SelectRoleGroupModel.findById(id); @@ -95,7 +101,19 @@ class SelectRolesUpdateCommand extends Command { await selectRolesMessage.edit({ content: message ? typeof message === "string" ? message : "" : undefined, embeds: message ? typeof message === "string" ? [] : [ message ] : undefined, - components: arrays.chunks(selectRoles, 5).map(roles => ({ + components: resetRoles ? [ + { + type: ComponentType.ActionRow, + components: [ + { + type: ComponentType.Button, + customId: MessageComponents.SelectRolesNoRolesButton, + style: ButtonStyle.Danger, + label: "Add Roles", + }, + ], + }, + ] : arrays.chunks(selectRoles, 5).map(roles => ({ type: ComponentType.ActionRow, components: roles.map(role => ({ customId: MessageComponents.SelectRolesButton + ":" + role.value, @@ -108,7 +126,7 @@ class SelectRolesUpdateCommand extends Command { }); } - return interaction.editReply(`I've updated the Select Roles Group **${ id }**.`); + return interaction.editReply(`I've updated the Select Roles Group [**${ id }**](${ selectRolesMessage.url }).`); } await interaction.editReply(`The Select Roles Group **${ id }** doesn't exist anymore.`); diff --git a/src/commands/music/play.ts b/src/commands/music/play.ts index 1d3140a9b..65e1d3416 100644 --- a/src/commands/music/play.ts +++ b/src/commands/music/play.ts @@ -3,7 +3,7 @@ * @copyright 2022 */ import { ApplicationCommandOptionType, ChatInputCommandInteraction } from "discord.js"; -import { AudioPlayerStatus, AudioResource, createAudioPlayer, createAudioResource, entersState, getVoiceConnection, joinVoiceChannel, NoSubscriberBehavior, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice"; +import { AudioPlayerStatus, AudioResource, createAudioPlayer, createAudioResource, DiscordGatewayAdapterCreator, entersState, getVoiceConnection, joinVoiceChannel, NoSubscriberBehavior, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice"; import { Client, Command, Logger } from "@bastion/tesseract"; import { music } from "@bastion/tesseract/typings/types.js"; import playDL from "@iamtraction/play-dl"; @@ -39,7 +39,7 @@ class PlayCommand extends Command { selfMute: false, guildId: interaction.guildId, channelId: interaction.channelId, - adapterCreator: interaction.guild.voiceAdapterCreator, + adapterCreator: interaction.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator, }); // voice connection events @@ -52,6 +52,8 @@ class PlayCommand extends Command { entersState(connection, VoiceConnectionStatus.Connecting, 5_000), ]); } catch (error) { + Logger.error(error); + // seems to be a real disconnect which SHOULDN'T be recovered from connection.destroy(); diff --git a/src/commands/translate.ts b/src/commands/translate.ts index cb968d135..ececb1c8b 100644 --- a/src/commands/translate.ts +++ b/src/commands/translate.ts @@ -4,6 +4,7 @@ */ import { ApplicationCommandOptionType, ChatInputCommandInteraction } from "discord.js"; import { Command } from "@bastion/tesseract"; +// eslint-disable-next-line @typescript-eslint/no-require-imports import translate = require("@iamtraction/google-translate"); class TranslateCommand extends Command { @@ -41,7 +42,10 @@ class TranslateCommand extends Command { // fetch the translation const response = await translate(text, { from, to }); - await interaction.editReply(response.text); + await interaction.editReply({ + content: `${ response.text } +-# Translated from ${ response.from.language.iso?.toUpperCase() } to ${ to.toUpperCase() }`, + }); } } diff --git a/src/listeners/messageDeleteBulk.ts b/src/listeners/messageDeleteBulk.ts index 542b8f4ec..10440fbdb 100644 --- a/src/listeners/messageDeleteBulk.ts +++ b/src/listeners/messageDeleteBulk.ts @@ -2,7 +2,7 @@ * @author TRACTION (iamtraction) * @copyright 2022 */ -import { Collection, GuildTextBasedChannel, Message, PartialMessage } from "discord.js"; +import { GuildTextBasedChannel, Message, OmitPartialGroupDMChannel, PartialMessage, ReadonlyCollection } from "discord.js"; import { Listener } from "@bastion/tesseract"; import { logGuildEvent } from "../utils/guilds.js"; @@ -12,7 +12,7 @@ class MessageDeleteBulkListener extends Listener<"messageDeleteBulk"> { super("messageDeleteBulk"); } - public async exec(messages: Collection | PartialMessage>, channel: GuildTextBasedChannel): Promise { + public async exec(messages: ReadonlyCollection | PartialMessage>>, channel: GuildTextBasedChannel): Promise { await logGuildEvent(channel.guild, { title: "Messages Cleared", fields: [ diff --git a/src/types.ts b/src/types.ts index 9390513d4..b5b447963 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,6 +17,16 @@ export namespace bastion { model?: string; maxTokens?: number; }; + gemini?: { + apiKey?: string; + model?: string; + maxOutputTokens?: number; + }; + anthropic?: { + apiKey?: string; + model?: string; + maxTokens?: number; + }; openWeatherMapApiKey?: string; tmdbApiKey?: string; trackerNetworkApiKey?: string; diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 84b711ba9..745c8c256 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -2,6 +2,7 @@ * @author TRACTION (iamtraction) * @copyright 2022 */ +// eslint-disable-next-line no-constant-binary-expression const frequency = (string: string): { [key: string]: number } => [ ...string ].reduce((a: { [key: string]: number }, c) => (a[c] = a[c] + 1 || 1) && a, {}); const toTitleCase = (string: string, splitSeparator = " ", joinSeprator = " "): string => {