discord: add /random command.
This commit is contained in:
@@ -88,7 +88,7 @@ class PostsController < ApplicationController
|
||||
end
|
||||
|
||||
def random
|
||||
@post = Post.user_tag_match(params[:tags]).random
|
||||
@post = Post.user_tag_match(params[:tags]).random(1).first
|
||||
raise ActiveRecord::RecordNotFound if @post.nil?
|
||||
authorize @post
|
||||
respond_with(@post) do |format|
|
||||
|
||||
@@ -22,16 +22,20 @@ class DiscordApiClient
|
||||
post("/applications/#{application_id}/guilds/#{guild_id}/commands", json)
|
||||
end
|
||||
|
||||
def get_channel(channel_id)
|
||||
get("/channels/#{channel_id}")
|
||||
def get_channel(channel_id, **options)
|
||||
get("/channels/#{channel_id}", **options)
|
||||
end
|
||||
|
||||
def me
|
||||
get("/users/@me")
|
||||
def me(**options)
|
||||
get("/users/@me", **options)
|
||||
end
|
||||
|
||||
def get(url)
|
||||
client.get("#{BASE_URL}/#{url}").parse
|
||||
def get(url, cache: nil, **options)
|
||||
if cache
|
||||
client.cache(cache).get("#{BASE_URL}/#{url}").parse
|
||||
else
|
||||
client.get("#{BASE_URL}/#{url}").parse
|
||||
end
|
||||
end
|
||||
|
||||
def post(url, data)
|
||||
|
||||
@@ -4,6 +4,7 @@ class DiscordSlashCommand
|
||||
COMMANDS = {
|
||||
count: DiscordSlashCommand::CountCommand,
|
||||
posts: DiscordSlashCommand::PostsCommand,
|
||||
random: DiscordSlashCommand::RandomCommand,
|
||||
}
|
||||
|
||||
# https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype
|
||||
@@ -15,6 +16,7 @@ class DiscordSlashCommand
|
||||
# https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptiontype
|
||||
module ApplicationCommandOptionType
|
||||
String = 3
|
||||
Integer = 4
|
||||
end
|
||||
|
||||
attr_reader :data, :discord
|
||||
@@ -51,14 +53,19 @@ class DiscordSlashCommand
|
||||
concerning :HelperMethods do
|
||||
# The parameters passed to the command. A hash.
|
||||
def params
|
||||
@params ||= data.dig(:data, :options).map do |opt|
|
||||
@params ||= data.dig(:data, :options).to_a.map do |opt|
|
||||
[opt[:name], opt[:value]]
|
||||
end.to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
# https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction
|
||||
# https://discord.com/developers/docs/interactions/slash-commands#interaction-response
|
||||
def respond_with(content = nil, type: 4, **options)
|
||||
def respond_with(content = nil, type: 4, posts: [], **options)
|
||||
if posts.present?
|
||||
embeds = posts.map { |post| DiscordSlashCommand::PostEmbed.new(post, self).to_h }
|
||||
options[:embeds] = embeds
|
||||
end
|
||||
|
||||
{
|
||||
type: type,
|
||||
data: {
|
||||
@@ -68,6 +75,10 @@ class DiscordSlashCommand
|
||||
}
|
||||
end
|
||||
|
||||
def channel
|
||||
discord.get_channel(data[:channel_id], cache: 1.minute)
|
||||
end
|
||||
|
||||
# Register the command with the Discord API (replacing it if it already exists).
|
||||
# https://discord.com/developers/docs/interactions/slash-commands#registering-a-command
|
||||
def register_slash_command
|
||||
|
||||
67
app/logical/discord_slash_command/post_embed.rb
Normal file
67
app/logical/discord_slash_command/post_embed.rb
Normal file
@@ -0,0 +1,67 @@
|
||||
class DiscordSlashCommand
|
||||
class PostEmbed
|
||||
attr_reader :post, :command
|
||||
|
||||
def initialize(post, command)
|
||||
@post = post
|
||||
@command = command
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
title: post.dtext_shortlink,
|
||||
url: Routes.url_for(post),
|
||||
timestamp: post.created_at.iso8601,
|
||||
color: embed_color,
|
||||
footer: embed_footer,
|
||||
image: {
|
||||
width: post.image_width,
|
||||
height: post.image_height,
|
||||
url: embed_image,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def embed_image
|
||||
if is_censored?
|
||||
nil
|
||||
elsif post.file_ext.match?(/jpe?g|png|gif/)
|
||||
post.file_url
|
||||
else
|
||||
post.preview_file_url
|
||||
end
|
||||
end
|
||||
|
||||
def embed_color
|
||||
if post.is_flagged?
|
||||
0xC41C19
|
||||
elsif post.is_pending?
|
||||
0x0000FF
|
||||
elsif post.parent_id.present?
|
||||
0xC0C000
|
||||
elsif post.has_active_children?
|
||||
0x00FF00
|
||||
elsif post.is_deleted?
|
||||
0xFFFFFF
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def embed_footer
|
||||
dimensions = "#{post.image_width}x#{post.image_height}"
|
||||
file_size = post.file_size.to_s(:human_size, precision: 4)
|
||||
text = "Rating: #{post.rating.upcase} | #{dimensions} (#{file_size} #{post.file_ext})"
|
||||
|
||||
{ text: text }
|
||||
end
|
||||
|
||||
def is_censored?
|
||||
post.rating != "s" && !is_nsfw_channel?
|
||||
end
|
||||
|
||||
def is_nsfw_channel?
|
||||
command.channel.fetch("nsfw")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -11,83 +11,26 @@ class DiscordSlashCommand
|
||||
end
|
||||
|
||||
def options
|
||||
[{
|
||||
name: "tags",
|
||||
description: "The tags to search",
|
||||
required: true,
|
||||
type: ApplicationCommandOptionType::String
|
||||
}]
|
||||
[
|
||||
{
|
||||
name: "tags",
|
||||
description: "The tags to search",
|
||||
type: ApplicationCommandOptionType::String
|
||||
},
|
||||
{
|
||||
name: "limit",
|
||||
description: "The number of posts to show (max 10)",
|
||||
type: ApplicationCommandOptionType::Integer
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def call
|
||||
tags = params[:tags]
|
||||
query = PostQueryBuilder.new(tags, User.anonymous).normalized_query
|
||||
limit = params.fetch(:limit, 3).clamp(1, 10)
|
||||
posts = Post.user_tag_match(tags, User.anonymous).limit(limit)
|
||||
|
||||
limit = query.find_metatag(:limit) || 3
|
||||
limit = limit.to_i.clamp(1, 10)
|
||||
posts = query.build.paginate(1, limit: limit)
|
||||
embeds = posts.map { |post| post_embed(post) }
|
||||
|
||||
respond_with(embeds: embeds)
|
||||
respond_with(posts: posts)
|
||||
end
|
||||
|
||||
def post_embed(post)
|
||||
{
|
||||
title: post.dtext_shortlink,
|
||||
url: Routes.url_for(post),
|
||||
timestamp: post.created_at.iso8601,
|
||||
color: post_embed_color(post),
|
||||
footer: post_embed_footer(post),
|
||||
image: {
|
||||
width: post.image_width,
|
||||
height: post.image_height,
|
||||
url: post_embed_image(post),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def post_embed_image(post, blur: 50)
|
||||
if is_censored?(post)
|
||||
nil
|
||||
elsif post.file_ext.match?(/jpe?g|png|gif/)
|
||||
post.file_url
|
||||
else
|
||||
post.preview_file_url
|
||||
end
|
||||
end
|
||||
|
||||
def post_embed_color(post)
|
||||
if post.is_flagged?
|
||||
0xC41C19
|
||||
elsif post.is_pending?
|
||||
0x0000FF
|
||||
elsif post.parent_id.present?
|
||||
0xC0C000
|
||||
elsif post.has_active_children?
|
||||
0x00FF00
|
||||
elsif post.is_deleted?
|
||||
0xFFFFFF
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def post_embed_footer(post)
|
||||
dimensions = "#{post.image_width}x#{post.image_height}"
|
||||
file_size = post.file_size.to_s(:human_size, precision: 4)
|
||||
text = "Rating: #{post.rating.upcase} | #{dimensions} (#{file_size} #{post.file_ext})"
|
||||
|
||||
{ text: text }
|
||||
end
|
||||
|
||||
def is_censored?(post)
|
||||
post.rating != "s" && !is_nsfw_channel?
|
||||
end
|
||||
|
||||
def is_nsfw_channel?
|
||||
discord.get_channel(data[:channel_id]).fetch("nsfw")
|
||||
end
|
||||
|
||||
memoize :is_nsfw_channel?
|
||||
end
|
||||
end
|
||||
|
||||
34
app/logical/discord_slash_command/random_command.rb
Normal file
34
app/logical/discord_slash_command/random_command.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
class DiscordSlashCommand
|
||||
class RandomCommand < DiscordSlashCommand
|
||||
def name
|
||||
"random"
|
||||
end
|
||||
|
||||
def description
|
||||
"Show a random post"
|
||||
end
|
||||
|
||||
def options
|
||||
[
|
||||
{
|
||||
name: "tags",
|
||||
description: "The tags to search",
|
||||
type: ApplicationCommandOptionType::String
|
||||
},
|
||||
{
|
||||
name: "limit",
|
||||
description: "The number of posts to show (max 10)",
|
||||
type: ApplicationCommandOptionType::Integer
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def call
|
||||
tags = params[:tags]
|
||||
limit = params.fetch(:limit, 1).clamp(1, 10)
|
||||
posts = Post.user_tag_match(tags, User.anonymous).random(limit)
|
||||
|
||||
respond_with(posts: posts)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -103,9 +103,7 @@ module PostSets
|
||||
end
|
||||
|
||||
def get_random_posts
|
||||
per_page.times.inject([]) do |all, _|
|
||||
all << ::Post.user_tag_match(tag_string).random
|
||||
end.compact.uniq
|
||||
::Post.user_tag_match(tag_string).random(per_page)
|
||||
end
|
||||
|
||||
def posts
|
||||
|
||||
@@ -1144,10 +1144,11 @@ class Post < ApplicationRecord
|
||||
end
|
||||
|
||||
module SearchMethods
|
||||
# returns one single post
|
||||
def random
|
||||
key = Digest::MD5.hexdigest(Time.now.to_f.to_s)
|
||||
random_up(key) || random_down(key)
|
||||
def random(n = 1)
|
||||
n.times.map do
|
||||
key = SecureRandom.hex(16)
|
||||
random_up(key) || random_down(key)
|
||||
end.compact.uniq
|
||||
end
|
||||
|
||||
def random_up(key)
|
||||
|
||||
Reference in New Issue
Block a user