bot.rb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. require 'bundler'
  2. require 'erb'
  3. require 'yaml'
  4. require 'json'
  5. require 'terminal-table'
  6. BOT_ENV = ENV.fetch('BOT_ENV') { 'development' }
  7. Bundler.require(:default, BOT_ENV)
  8. require 'active_record'
  9. require_relative 'lib/emoji'
  10. # Constants: such as roles and channel ids
  11. # Users
  12. APP_BOT = 627702340018896896
  13. # Roles
  14. ADMINS = 308250685554556930
  15. # Channels
  16. CHAR_CHANNEL = 594244240020865035
  17. # Images
  18. HAP_ROTOM = "https://static.pokemonpets.com/images/monsters-images-800-800/479-Rotom.png"
  19. # URLs
  20. APP_FORM = "https://docs.google.com/forms/d/e/1FAIpQLSfryXixX3aKBNQxZT8xOfWzuF02emkJbqJ1mbMGxZkwCvsjyA/viewform"
  21. # ---
  22. Dotenv.load if BOT_ENV != 'production'
  23. db_yml = File.open('config/database.yml') do |erb|
  24. ERB.new(erb.read).result
  25. end
  26. db_config = YAML.safe_load(db_yml)[BOT_ENV]
  27. ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
  28. ActiveRecord::Base.establish_connection(
  29. adapter: 'postgresql',
  30. host: db_config.fetch('host') { 'localhost' },
  31. database: db_config['database'],
  32. user: db_config['user'],
  33. password: db_config['password']
  34. )
  35. Dir['app/**/*.rb'].each { |f| require File.join(File.expand_path(__dir__), f) }
  36. token = ENV['DISCORD_BOT_TOKEN']
  37. bot = Discordrb::Bot.new(token: token)
  38. # Methods: define basic methods here
  39. def check_user(event)
  40. content = event.message.content
  41. edit_url = /Edit\sKey\s\(ignore\):\s([\s\S]*)/.match(content)
  42. if user_id = (/<@([0-9]+)>/.match(content))
  43. user = User.find_by(id: user_id[1])
  44. allowed_characters = (user.level / 10 + 1)
  45. characters = Character.where(user_id: user_id[1]).count
  46. if characters < allowed_characters && characters < 6
  47. event.message.react(Emoji::Y)
  48. event.message.react(Emoji::N)
  49. else
  50. event.server.member(user_id[1]).dm("You have too many characters!\nPlease deactivate and try again #{edit_url[1]}")
  51. event.message.delete
  52. end
  53. else
  54. event.message.edit("#{content}\n\nI don't know this user!")
  55. end
  56. end
  57. def edit_character(params, member)
  58. key_mapping = {
  59. "Submitted by" => "user_id",
  60. " >>> **Characters Name**" => "name",
  61. "**Species**" => "species",
  62. "**Type**" => "types",
  63. "**Age**" => "age",
  64. "**Weight**" => "weight",
  65. "**Height**" => "height",
  66. "**Gender**" => "gender",
  67. "**Sexual Orientation**" => "orientation",
  68. "**Relationship Status**" => "relationship",
  69. "**URL to your character's appearance**" => "image_url",
  70. "**Attacks**" => "attacks",
  71. "**Custom Attack Description**" => "custom_attack",
  72. "**Likes**" => "likes",
  73. "**Dislikes**" => "dislikes",
  74. "**Personality**" => "personality",
  75. "**Rumors**" => "rumors",
  76. "**Backstory**" => "backstory",
  77. "**Other**" => "other",
  78. "Edit Key (ignore)" => "edit_url",
  79. }
  80. hash = {}
  81. params.map do |item|
  82. next if item.empty?
  83. key,value = item.split(": ")
  84. db_column = key_mapping[key]
  85. if v = value.match(/<@([0-9]+)>/)
  86. hash[db_column] = v[1]
  87. else
  88. hash[db_column] = value
  89. end
  90. end
  91. # Should we add this to the form, and allow NPCs to be created the same way?
  92. hash["active"] = 'Active'
  93. if image_url = hash["image_url"]
  94. hash = hash.reject { |k| k == "image_url" }
  95. end
  96. if custom_attack = hash["custom_attack"]
  97. hash = hash.reject { |k| k == "custom_attack" }
  98. end
  99. if char = Character.find_by(edit_url: hash["edit_url"])
  100. char.update!(hash)
  101. character = Character.find_by(edit_url: hash["edit_url"])
  102. else
  103. character = Character.create(hash)
  104. end
  105. edit_images(image_url, character.id, 'sfw', 'primary') if image_url
  106. character_embed(character, image_url, member)
  107. end
  108. def edit_images(image_url, character_id, category, keyword)
  109. unless CharImages.where(char_id: character_id).find_by(url: image_url)
  110. CharImages.create(char_id: character_id, url: image_url, category: category, keyword: keyword)
  111. end
  112. end
  113. def character_embed(character, image, member)
  114. fields = []
  115. user = "#{member.name}##{member.tag}"
  116. fields.push({name: 'Species', value: character.species, inline: true}) if character.species
  117. fields.push({name: 'Type', value: character.types, inline: true}) if character.types
  118. fields.push({name: 'Age', value: character.age, inline: true}) if character.age
  119. fields.push({name: 'Weight', value: character.weight, inline: true}) if character.weight
  120. fields.push({name: 'Height', value: character.height, inline: true}) if character.height
  121. fields.push({name: 'Gender', value: character.gender, inline: true}) if character.gender
  122. fields.push({name: 'Sexual Orientation', value: character.orientation, inline: true}) if character.orientation
  123. fields.push({name: 'Relationship Status', value: character.relationship, inline: true}) if character.relationship
  124. fields.push({name: 'Attacks', value: character.attacks}) if character.attacks
  125. fields.push({name: 'Likes', value: character.likes}) if character.likes
  126. fields.push({name: 'Dislikes', value: character.dislikes}) if character.dislikes
  127. fields.push({name: 'Rumors', value: character.rumors}) if character.rumors
  128. fields.push({name: 'Backstory', value: character.backstory}) if character.backstory
  129. fields.push({name: 'Other', value: character.other}) if character.other
  130. embed = Embed.new(
  131. footer: {
  132. text: "Created by #{user} | #{character.active}"
  133. },
  134. title: character.name,
  135. fields: fields
  136. )
  137. embed.description = character.personality if character.personality
  138. embed.thumbnail = { url: image } if image
  139. embed.color = member.color.combined if member.color.combined
  140. embed
  141. end
  142. # ---
  143. command_list = []
  144. # Commands: structure basic bot commands here
  145. command_list.push(["pkmn-hello","a simple test command to make the bot say hi."])
  146. hello = Command.new(:hello) do |event|
  147. user = event.author.nickname || event.author.name
  148. greetings = [
  149. "Hi there, #{user}",
  150. "Greetings #{user}, you lovable bum",
  151. "Alola, #{user}",
  152. "Hello, #{user}! The Guildmasters have been waiting",
  153. "#{user} would like to battle!"
  154. ]
  155. Embed.new(
  156. description: greetings.sample,
  157. color: event.author.color.combined,
  158. thumbnail: {
  159. url: HAP_ROTOM
  160. }
  161. )
  162. end
  163. help = Command.new(:help) do |event,extra|
  164. user = event.author.nickname || event.author.name
  165. fields = []
  166. command_list.each do |item|
  167. fields.push({name: item[0], value: item[1]})
  168. end
  169. Embed.new(
  170. color: "#73FE49",
  171. title: "List of Character Commands",
  172. description: "Basic list of commands and what they do!",
  173. fields: fields
  174. )
  175. end
  176. command_list.push(["pkmn-matchup <type>","Shows the types that are strong and weak to the given type."])
  177. matchup = Command.new(:matchup) do |event, type|
  178. channel = event.channel.id
  179. file = "images/Type #{type.capitalize}.png"
  180. if File.exists?(file)
  181. bot.send_file(channel, File.open(file, 'r'))
  182. else
  183. bot.respond("I do not know this pokemon type! Please try again!")
  184. end
  185. end
  186. command_list.insert(0,["pkmn-app <name>","Starts the process for a new character appication or to edit an existing one. Dont worry. Any other commands needed for this will be listed in the responces!"])
  187. app = Command.new(:app) do |event, name|
  188. user = event.author
  189. if name
  190. if character = Character.where(user_id: user.id).find_by(name: name)
  191. edit_url = APP_FORM + character.edit_url
  192. event.respond("OK, #{user.name}! I'll send you what you need to edit #{name}")
  193. user.dm("You may edit #{name} here:\n#{edit_url}")
  194. else
  195. event.respond("I didn't find your character, #{name}\nIf you want to start a new app, please use `pkmn-app`")
  196. end
  197. else
  198. event.respond("You want to start a new character application?\nGreat! I'll dm you instructions")
  199. user.dm("Hi, #{user.name}\nYou can start your application here:\n#{APP_FORM}\n\nYour key is: #{user.id}\nOnce complete, your application will submitted to the admins for approval!")
  200. end
  201. end
  202. # ---
  203. commands = [
  204. app,
  205. hello,
  206. help,
  207. matchup
  208. ]
  209. # This will trigger on every message sent in discord
  210. bot.message do |event|
  211. content = event.message.content
  212. if (match = /^pkmn-(\w+)/.match(content))
  213. command = match[1]
  214. if cmd = commands.detect { |c| c.name == command.to_sym }
  215. reply = cmd.call(content, event)
  216. if reply.is_a? Embed
  217. event.send_embed("", reply)
  218. elsif reply
  219. event.respond(reply)
  220. else
  221. event.respond("Something went wrong!")
  222. end
  223. end
  224. end
  225. if event.author.id == APP_BOT
  226. check_user(event)
  227. end
  228. end
  229. # This will trigger on every reaction is added in discord
  230. bot.reaction_add do |event|
  231. content = event.message.content
  232. if event.message.author.id == APP_BOT
  233. maj = event.server.roles.find{ |r| r.id == ADMINS }.members.count / 2
  234. maj = 1
  235. if event.message.reacted_with(Emoji::Y).count > maj
  236. msg = event.message.content.split("\n")
  237. uid = /<@([0-9]+)>/.match(event.message.content)
  238. member = event.server.member(uid[1])
  239. embed = edit_character(msg, member)
  240. if embed
  241. event.message.delete
  242. bot.send_message(
  243. CHAR_CHANNEL,
  244. "Character Approved!",
  245. false,
  246. embed
  247. )
  248. else
  249. event.respond("Something went wrong")
  250. end
  251. elsif event.message.reacted_with(Emoji::N).count > maj
  252. message = event.message.content
  253. split_message = message.split("\n")
  254. i = 0
  255. split_message.each do |row|
  256. if row.match(/\*\*/)
  257. if row.match(/>>>/)
  258. row.insert 5, "#{Emoji::ALL[i]} "
  259. i += 1
  260. else
  261. row.insert 0, "#{Emoji::ALL[i]} "
  262. i += 1
  263. end
  264. end
  265. end
  266. edited_message = split_message.join("\n")
  267. new_message = "**_APPLICATION REJECTED!!_**\n--------------\n\n#{edited_message}\n\n\nPlease indicate what needs to be updated with the corresponding reactions!\nWhen you're done hit #{Emoji::CHECK}, or to dismiss hit #{Emoji::CROSS}"
  268. event.message.delete
  269. rejected = event.respond(new_message)
  270. j = 0
  271. i.times do
  272. rejected.react(Emoji::ALL[j])
  273. j += 1
  274. end
  275. rejected.react(Emoji::CHECK)
  276. rejected.react(Emoji::CROSS)
  277. end
  278. end
  279. if event.message.from_bot? && content.match(/\*\*\_APPLICATION\sREJECTED\!\!\_\*\*/)
  280. if event.message.reacted_with(Emoji::CHECK).count > 1
  281. reactions = event.message.reactions
  282. edit_url = /Edit\sKey\s\(ignore\):\s([\s\S]*)/.match(content)
  283. user_id = /<@([0-9]+)>/.match(content)
  284. member = event.server.member(user_id[1])
  285. message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n#{APP_FORM}#{edit_url[1]}"
  286. rows = reactions.count - 2
  287. i = 0
  288. rows.times do
  289. if reactions[Emoji::ALL[i]].count > 1
  290. row = /#{Emoji::ALL[i]}\s(.*)/.match(content)
  291. message += "\n> #{row[1]}"
  292. end
  293. i += 1
  294. end
  295. temp_message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n[users url goes here]"
  296. message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n#{APP_FORM}#{edit_url[1]}"
  297. event.message.delete
  298. event.send_temporary_message(temp_message, 15)
  299. member.dm(message)
  300. elsif event.message.reacted_with(Emoji::CROSS).count > 1
  301. event.message.delete
  302. end
  303. end
  304. end
  305. # This will trigger on every reaction is removed in discord
  306. bot.reaction_remove do |event|
  307. end
  308. # This will trigger when a member is updated
  309. bot.member_update do |event|
  310. end
  311. # This will trigger when anyone joins the server
  312. bot.member_join do |event|
  313. end
  314. # This will trigger when anyone leaves the server
  315. bot.member_leave do |event|
  316. end
  317. # This will trigger when anyone is banned from the server
  318. bot.user_ban do |event|
  319. end
  320. # This will trigger when anyone is un-banned from the server
  321. bot.user_unban do |event|
  322. end
  323. bot.run