unbot/main.cr
2026-04-19 17:27:33 +02:00

184 lines
4.9 KiB
Crystal

require "discordcr"
require "json"
require "uuid"
require "./responses"
require "./reactions"
token = ENV["DISCORD_BOT_TOKEN"]? || raise "Missing DISCORD_BOT_TOKEN env variable"
puts("Bot #{token}")
client = Discord::Client.new(
token: "Bot #{token}",
client_id: 1475668463363293398_u64
)
DICT_FILE = "./dictionary.json"
USER_LIMIT = 100
def load_dict
if File.exists?(DICT_FILE)
JSON.parse(File.read(DICT_FILE)).as_h.transform_values do |defs|
defs.as_a.map do |d|
{
"id" => d["id"].as_s,
"definition" => d["definition"].as_s,
"author" => d["author"].as_s,
"upvotes" => d["upvotes"].as_i,
"downvotes" => d["downvotes"].as_i
}
end
end
else
{} of String => Array(Hash(String, String|Int32))
end
end
def save_dict(dict)
File.write(DICT_FILE, dict.to_json)
end
dictionary = load_dict
def user_def_count(dict, username)
dict.values.flatten.count { |d| d["author"] == username }
end
client.on_message_create do |payload|
content = payload.content
args = content.split(" ", 3)
command = args[0]?
user = payload.author.username
if payload.author.bot
next
end
case command
when "!add"
word = args[1]?
definition = args[2]?
if word.nil? || definition.nil?
client.create_message(payload.channel_id, "usage: !add <word> <definition>")
next
end
if user_def_count(dictionary, user) >= USER_LIMIT
client.create_message(payload.channel_id, "#{user}, you reached the limit of #{USER_LIMIT} definitions.")
next
end
dictionary[word] ||= [] of Hash(String, String | Int32)
dictionary[word] << {
"id" => UUID.random.to_s,
"definition" => definition,
"author" => user,
"upvotes" => 0,
"downvotes" => 0
}
save_dict(dictionary)
client.create_message(payload.channel_id, "added definition for **#{word}** by #{user}.")
when "!define"
word = args[1]?
if word.nil?
client.create_message(payload.channel_id, "usage: !define <word>")
next
end
defs = dictionary[word]?
if defs
text = defs.map_with_index do |d, i|
"#{i+1}. #{d["definition"]} (by #{d["author"]}) 👍#{d["upvotes"]} 👎#{d["downvotes"]}"
end.join("\n")
client.create_message(payload.channel_id, "**#{word}**:\n#{text}")
else
client.create_message(payload.channel_id, "no definition for **#{word}**.")
end
when "!list"
word = args[1]?
if word.nil?
client.create_message(payload.channel_id, "usage: !list <word>")
next
end
defs = dictionary[word]?
if defs
text = defs.map_with_index do |d, i|
"#{i+1}. #{d["definition"]} (by #{d["author"]}) 👍#{d["upvotes"]} 👎#{d["downvotes"]}"
end.join("\n")
client.create_message(payload.channel_id, "**#{word}** definitions:\n#{text}")
else
client.create_message(payload.channel_id, "no definitions found.")
end
when "!upvote", "!downvote"
word = args[1]?
index = args[2]? ? args[2].to_i : nil
if word.nil? || index.nil?
client.create_message(payload.channel_id, "usage: #{command} <word> <definition_number>")
next
end
defs = dictionary[word]?
if defs && index > 0 && index <= defs.size
target = defs[index - 1]
if command == "!upvote"
target["upvotes"] = target["upvotes"].to_i + 1
else
target["downvotes"] = target["downvotes"].to_i + 1
end
save_dict(dictionary)
client.create_message(payload.channel_id, "updated votes for definition #{index} of **#{word}**.")
else
client.create_message(payload.channel_id, "invalid word or definition number.")
end
when "!random"
if dictionary.empty?
client.create_message(payload.channel_id, "nichectionary is empty.")
next
end
word = dictionary.keys.sample
defs = dictionary[word]
def_choice = defs.sample
client.create_message(payload.channel_id,
"**#{word}**:\n#{def_choice["definition"]} (by #{def_choice["author"]}) 👍#{def_choice["upvotes"]} 👎#{def_choice["downvotes"]}"
)
when "!search"
term = args[1]?
if term.nil?
client.create_message(payload.channel_id, "usage: !search <term>")
next
end
matches = dictionary.select { |w, _| w.includes?(term) }
if matches.empty?
client.create_message(payload.channel_id, "no words matched `#{term}`.")
next
end
text = matches.keys.join(", ")
client.create_message(payload.channel_id, "words matching `#{term}`: #{text}")
end
Reactions.check_message(content).each do |emoji|
client.create_reaction(payload.channel_id, payload.id, emoji)
end
preset_reply = Responses.check_message(content)
if preset_reply
client.trigger_typing_indicator(payload.channel_id)
sleep 500.milliseconds
client.create_message(payload.channel_id, preset_reply, message_reference: payload.message_reference())
next
end
end
client.run