|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require 'discordrb' |
| 4 | + |
| 5 | +bot = Discordrb::Bot.new(token: ENV.fetch('BOT_TOKEN', nil), intents: %i[servers server_emojis server_messages]) |
| 6 | + |
| 7 | +bot.register_application_command(:container, 'An example of a container component.', server_id: ENV.fetch('SERVER_ID', nil)) do |option| |
| 8 | + option.boolean(:color, 'Whether the container should include an accent color.', required: false) |
| 9 | +end |
| 10 | + |
| 11 | +bot.application_command(:container) do |event| |
| 12 | + # Transform the first 15 emojis from the server our command is |
| 13 | + # called from into the formart of: "mention - name **{Integer}**". |
| 14 | + emojis = event.server.emojis.values.shuffle.take(15).map do |emoji| |
| 15 | + "#{emoji.mention} — #{emoji.name} **(#{rand(2001..5001)})**\n" |
| 16 | + end |
| 17 | + |
| 18 | + # The `has_components` flag must be manually set to true to enable uikit components. |
| 19 | + event.respond(has_components: true) do |_, view| |
| 20 | + # A new container is added to contain other components. We don't have to do this, |
| 21 | + # since any non-interactive component can be used as top level component. |
| 22 | + view.container do |container| |
| 23 | + # A section must have either a thumbnail or a button. This is currently |
| 24 | + # the only case where a button can be used without being in an action row. |
| 25 | + container.section do |section| |
| 26 | + section.thumbnail(url: event.server.icon_url) |
| 27 | + section.text_display(text: "### Emoji Statistics for #{event.server.name}") |
| 28 | + section.text_display(text: 'These are the fake emoji statistics for your server.') |
| 29 | + end |
| 30 | + |
| 31 | + # Unlike embeds, if the accent color isn't set, the container simply won't have an accent color. |
| 32 | + container.color = rand(0..0xFFFFFF) if event.options['color'] |
| 33 | + |
| 34 | + # A seperator can appear as a thin, and translucent line when setting `divider` to true. Otherwise, |
| 35 | + # the seperator can function as an invisible barrier to proivde padding between components. |
| 36 | + container.seperator(divider: true, spacing: :small) |
| 37 | + |
| 38 | + # A text display is a container for text. Similar to the `content` field, you can use mentions, MDX, etc. |
| 39 | + container.text_display(text: emojis.empty? ? 'No Emojis!' : emojis.join) |
| 40 | + |
| 41 | + # Try setting the spacing to `:large` to have a bigger gap between the other components. |
| 42 | + container.seperator(divider: true, spacing: :small) |
| 43 | + |
| 44 | + # We clear the existing emojis array and add a random emoji to the emojis array. |
| 45 | + emojis.clear && 3.times { emojis << event.server.emojis.values.sample } |
| 46 | + |
| 47 | + # We can add a select menu inside of our containter as shown here. Since this is an action row, we could've |
| 48 | + # chosen to add buttons here instead, but for the sake of the example, we'll stick to a select menu. |
| 49 | + container.row do |row| |
| 50 | + row.select_menu(custom_id: 'emojis', placeholder: 'Pick a statistic type...', min_values: 1) do |menu| |
| 51 | + menu.option(label: 'Reaction', value: 'Reaction', description: 'View reaction statistics.', emoji: emojis.pop) |
| 52 | + menu.option(label: 'Message', value: 'Message', description: 'View message statistics.', emoji: emojis.pop) |
| 53 | + menu.option(label: 'Lowest', value: 'Lowest', description: 'View the boring emojis.', emoji: emojis.pop) |
| 54 | + end |
| 55 | + end |
| 56 | + end |
| 57 | + end |
| 58 | +end |
| 59 | + |
| 60 | +# This doesn't actually display any stats, but returns a placeholder message instead. |
| 61 | +bot.select_menu(custom_id: 'emojis') do |event| |
| 62 | + case event.values.first |
| 63 | + when 'Reaction', 'Message' |
| 64 | + event.respond(content: "You're viewing stats for #{event.values.first}s!", ephemeral: true) |
| 65 | + when 'Lowest' |
| 66 | + event.respond(content: "You're viewing very boring stats!", ephemeral: true) |
| 67 | + else |
| 68 | + event.respond(content: 'What kind of stats...', ephemeral: true) |
| 69 | + end |
| 70 | +end |
| 71 | + |
| 72 | +# The second example is a little more generic and uses a standard message sent to a channel. |
| 73 | +bot.message(content: '!sample') do |event| |
| 74 | + # Any of the components used below can be used within a container as well. |
| 75 | + event.send_message!(has_components: true) do |_, view| |
| 76 | + view.text_display(text: <<~CONTENT) |
| 77 | + This is a text display component! Any markdown that can be used in the `content` field can also be used here.\n |
| 78 | + ~~strikethrough~~ **bold text** *italics* ||spoiler|| `code` __underline__ [masked link](https://youtu.be/dQw4w9WgXcQ)\n |
| 79 | + ```ruby |
| 80 | + puts("Hello World!") |
| 81 | + ``` |
| 82 | + CONTENT |
| 83 | + |
| 84 | + # Just like in the example above, we can set `:divider` to true in order to generate a barrier. |
| 85 | + view.seperator(divider: true, spacing: :large) |
| 86 | + |
| 87 | + # A media gallery is a container for multiple pieces of media, such as videos, GIFs or staic images. |
| 88 | + # You can add optionally add alt text via the `description:` argument and spoiler each media item. |
| 89 | + view.media_gallery do |gallery| |
| 90 | + gallery.item(url: 'https://static.wikitide.net/sillycatsbookwiki/f/f7/Apple_Cat.jpg', spoiler: true) |
| 91 | + gallery.item(url: 'https://media.tenor.com/JNrPF3XuHXIAAAAd/java-duke.gif', description: 'Factory') |
| 92 | + end |
| 93 | + |
| 94 | + # A Section allows you to group together text display components and pair them with an accessory. |
| 95 | + # In the emoji stats example above, we had a section with a thumbnail component. At the time of writing, |
| 96 | + # a section must contain either a thumbnail or a button. |
| 97 | + view.section do |section| |
| 98 | + section.text_display(text: 'This is text from a section. This section has a button instead of a thumbnail.') |
| 99 | + |
| 100 | + section.button(label: 'Delete', style: :danger, custom_id: 'delete_message', emoji: 577658883468689409) |
| 101 | + end |
| 102 | + end |
| 103 | +end |
| 104 | + |
| 105 | +# Delete the message that was sent in response to "!sample". |
| 106 | +bot.button(custom_id: 'delete_message') do |event| |
| 107 | + event.respond(content: 'Successfully deleted the message.', ephemeral: true) |
| 108 | + |
| 109 | + event.message.delete |
| 110 | +end |
| 111 | + |
| 112 | +# This last example shows how a file component looks. |
| 113 | +bot.message(content: '!file') do |event| |
| 114 | + # Any attachments that are provided must be manually exposed via the component system. |
| 115 | + event.send_message!(attachments: [File.open('data/music.mp3', 'rb')], has_components: true) do |_, view| |
| 116 | + view.container do |container| |
| 117 | + # All components accept an `id:` KWARG. This ID can be any 32-bit integer. This is |
| 118 | + # not to be confused with the `custom_id:` parameter. |
| 119 | + container.section(id: rand(500..600)) do |section| |
| 120 | + section.thumbnail(url: 'https://cdn.discordapp.com/icons/81384788765712384/a363a84e969bcbe1353eb2fdfb2e50e6.webp') |
| 121 | + |
| 122 | + section.text_display(text: '### Musical File') |
| 123 | + |
| 124 | + # All of the information below can be found if you inspect the audio file's metadata. |
| 125 | + section.text_display(text: <<~CONTENT) |
| 126 | + > **Title:** Discordrb Theme |
| 127 | + > **Composed:** <t:1472839597:R> |
| 128 | + > **Album:** Discord API Music |
| 129 | + CONTENT |
| 130 | + end |
| 131 | + |
| 132 | + # Try setting `spoiler` to true in order to spoiler the file. |
| 133 | + container.file(url: 'attachment://music.mp3', spoiler: false) |
| 134 | + end |
| 135 | + end |
| 136 | +end |
| 137 | + |
| 138 | +bot.run |
0 commit comments