discord: add /random command.
This commit is contained in:
@@ -88,7 +88,7 @@ class PostsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def random
|
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?
|
raise ActiveRecord::RecordNotFound if @post.nil?
|
||||||
authorize @post
|
authorize @post
|
||||||
respond_with(@post) do |format|
|
respond_with(@post) do |format|
|
||||||
|
|||||||
@@ -22,16 +22,20 @@ class DiscordApiClient
|
|||||||
post("/applications/#{application_id}/guilds/#{guild_id}/commands", json)
|
post("/applications/#{application_id}/guilds/#{guild_id}/commands", json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_channel(channel_id)
|
def get_channel(channel_id, **options)
|
||||||
get("/channels/#{channel_id}")
|
get("/channels/#{channel_id}", **options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def me
|
def me(**options)
|
||||||
get("/users/@me")
|
get("/users/@me", **options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(url)
|
def get(url, cache: nil, **options)
|
||||||
client.get("#{BASE_URL}/#{url}").parse
|
if cache
|
||||||
|
client.cache(cache).get("#{BASE_URL}/#{url}").parse
|
||||||
|
else
|
||||||
|
client.get("#{BASE_URL}/#{url}").parse
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def post(url, data)
|
def post(url, data)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class DiscordSlashCommand
|
|||||||
COMMANDS = {
|
COMMANDS = {
|
||||||
count: DiscordSlashCommand::CountCommand,
|
count: DiscordSlashCommand::CountCommand,
|
||||||
posts: DiscordSlashCommand::PostsCommand,
|
posts: DiscordSlashCommand::PostsCommand,
|
||||||
|
random: DiscordSlashCommand::RandomCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype
|
# 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
|
# https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptiontype
|
||||||
module ApplicationCommandOptionType
|
module ApplicationCommandOptionType
|
||||||
String = 3
|
String = 3
|
||||||
|
Integer = 4
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :data, :discord
|
attr_reader :data, :discord
|
||||||
@@ -51,14 +53,19 @@ class DiscordSlashCommand
|
|||||||
concerning :HelperMethods do
|
concerning :HelperMethods do
|
||||||
# The parameters passed to the command. A hash.
|
# The parameters passed to the command. A hash.
|
||||||
def params
|
def params
|
||||||
@params ||= data.dig(:data, :options).map do |opt|
|
@params ||= data.dig(:data, :options).to_a.map do |opt|
|
||||||
[opt[:name], opt[:value]]
|
[opt[:name], opt[:value]]
|
||||||
end.to_h.with_indifferent_access
|
end.to_h.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
# https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction
|
# https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction
|
||||||
# https://discord.com/developers/docs/interactions/slash-commands#interaction-response
|
# 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,
|
type: type,
|
||||||
data: {
|
data: {
|
||||||
@@ -68,6 +75,10 @@ class DiscordSlashCommand
|
|||||||
}
|
}
|
||||||
end
|
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).
|
# Register the command with the Discord API (replacing it if it already exists).
|
||||||
# https://discord.com/developers/docs/interactions/slash-commands#registering-a-command
|
# https://discord.com/developers/docs/interactions/slash-commands#registering-a-command
|
||||||
def register_slash_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
|
end
|
||||||
|
|
||||||
def options
|
def options
|
||||||
[{
|
[
|
||||||
name: "tags",
|
{
|
||||||
description: "The tags to search",
|
name: "tags",
|
||||||
required: true,
|
description: "The tags to search",
|
||||||
type: ApplicationCommandOptionType::String
|
type: ApplicationCommandOptionType::String
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
name: "limit",
|
||||||
|
description: "The number of posts to show (max 10)",
|
||||||
|
type: ApplicationCommandOptionType::Integer
|
||||||
|
}
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
tags = params[:tags]
|
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
|
respond_with(posts: posts)
|
||||||
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)
|
|
||||||
end
|
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
|
||||||
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
|
end
|
||||||
|
|
||||||
def get_random_posts
|
def get_random_posts
|
||||||
per_page.times.inject([]) do |all, _|
|
::Post.user_tag_match(tag_string).random(per_page)
|
||||||
all << ::Post.user_tag_match(tag_string).random
|
|
||||||
end.compact.uniq
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def posts
|
def posts
|
||||||
|
|||||||
@@ -1144,10 +1144,11 @@ class Post < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
module SearchMethods
|
module SearchMethods
|
||||||
# returns one single post
|
def random(n = 1)
|
||||||
def random
|
n.times.map do
|
||||||
key = Digest::MD5.hexdigest(Time.now.to_f.to_s)
|
key = SecureRandom.hex(16)
|
||||||
random_up(key) || random_down(key)
|
random_up(key) || random_down(key)
|
||||||
|
end.compact.uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
def random_up(key)
|
def random_up(key)
|
||||||
|
|||||||
Reference in New Issue
Block a user