discord: add /random command.

This commit is contained in:
evazion
2021-03-11 21:23:20 -06:00
parent b79bd8407f
commit 698be2d0e4
8 changed files with 146 additions and 88 deletions

View File

@@ -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|

View File

@@ -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)

View File

@@ -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

View 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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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)