bot.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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. # Commands: structure basic bot commands here
  144. hello = Command.new(:hello) do |event|
  145. user = event.author.nickname || event.author.name
  146. greetings = [
  147. "Hi there, #{user}",
  148. "Greetings #{user}, you lovable bum",
  149. "Alola, #{user}",
  150. "Hello, #{user}! The Guildmasters have been waiting",
  151. "#{user} would like to battle!"
  152. ]
  153. Embed.new(
  154. description: greetings.sample,
  155. color: event.author.color.combined,
  156. thumbnail: {
  157. url: HAP_ROTOM
  158. }
  159. )
  160. end
  161. matchup = Command.new(:matchup) do |event, type|
  162. channel = event.channel.id
  163. file = "images/Type #{type.capitalize}.png"
  164. if File.exists?(file)
  165. bot.send_file(channel, File.open(file, 'r'))
  166. else
  167. bot.respond("I do not know this pokemon type! Please try again!")
  168. end
  169. end
  170. app = Command.new(:app) do |event, name|
  171. user = event.author
  172. if name
  173. if character = Character.where(user_id: user.id).find_by(name: name)
  174. edit_url = APP_FORM + character.edit_url
  175. event.respond("OK, #{user.name}! I'll send you what you need to edit #{name}")
  176. user.dm("You may edit #{name} here:\n#{edit_url}")
  177. else
  178. event.respond("I didn't find your character, #{name}\nIf you want to start a new app, please use `pkmn-app`")
  179. end
  180. else
  181. event.respond("You want to start a new character application?\nGreat! I'll dm you instructions")
  182. 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!")
  183. end
  184. end
  185. # ---
  186. commands = [
  187. hello,
  188. matchup,
  189. app
  190. ]
  191. # This will trigger on every message sent in discord
  192. bot.message do |event|
  193. content = event.message.content
  194. if (match = /^pkmn-(\w+)/.match(content))
  195. command = match[1]
  196. if cmd = commands.detect { |c| c.name == command.to_sym }
  197. reply = cmd.call(content, event)
  198. if reply.is_a? Embed
  199. event.send_embed("", reply)
  200. elsif reply
  201. event.respond(reply)
  202. else
  203. event.respond("Something went wrong!")
  204. end
  205. end
  206. end
  207. if event.author.id == APP_BOT
  208. check_user(event)
  209. end
  210. end
  211. # This will trigger on every reaction is added in discord
  212. bot.reaction_add do |event|
  213. content = event.message.content
  214. if event.message.author.id == APP_BOT
  215. maj = event.server.roles.find{ |r| r.id == ADMINS }.members.count / 2
  216. maj = 1
  217. if event.message.reacted_with(Emoji::Y).count > maj
  218. msg = event.message.content.split("\n")
  219. uid = /<@([0-9]+)>/.match(event.message.content)
  220. member = event.server.member(uid[1])
  221. embed = edit_character(msg, member)
  222. if embed
  223. event.message.delete
  224. bot.send_message(
  225. CHAR_CHANNEL,
  226. "Character Approved!",
  227. false,
  228. embed
  229. )
  230. else
  231. event.respond("Something went wrong")
  232. end
  233. elsif event.message.reacted_with(Emoji::N).count > maj
  234. message = event.message.content
  235. split_message = message.split("\n")
  236. i = 0
  237. split_message.each do |row|
  238. if row.match(/\*\*/)
  239. if row.match(/>>>/)
  240. row.insert 5, "#{Emoji::ALL[i]} "
  241. i += 1
  242. else
  243. row.insert 0, "#{Emoji::ALL[i]} "
  244. i += 1
  245. end
  246. end
  247. end
  248. edited_message = split_message.join("\n")
  249. 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}"
  250. event.message.delete
  251. rejected = event.respond(new_message)
  252. j = 0
  253. i.times do
  254. rejected.react(Emoji::ALL[j])
  255. j += 1
  256. end
  257. rejected.react(Emoji::CHECK)
  258. rejected.react(Emoji::CROSS)
  259. end
  260. end
  261. if event.message.from_bot? && content.match(/\*\*\_APPLICATION\sREJECTED\!\!\_\*\*/)
  262. if event.message.reacted_with(Emoji::CHECK).count > 1
  263. reactions = event.message.reactions
  264. edit_url = /Edit\sKey\s\(ignore\):\s([\s\S]*)/.match(content)
  265. user_id = /<@([0-9]+)>/.match(content)
  266. member = event.server.member(user_id[1])
  267. message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n#{APP_FORM}#{edit_url[1]}"
  268. rows = reactions.count - 2
  269. i = 0
  270. rows.times do
  271. if reactions[Emoji::ALL[i]].count > 1
  272. row = /#{Emoji::ALL[i]}\s(.*)/.match(content)
  273. message += "\n> #{row[1]}"
  274. end
  275. i += 1
  276. end
  277. temp_message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n[users url goes here]"
  278. message = "Your application has been rejected!\nPlease fix the following lines, and resubmit here:\n#{APP_FORM}#{edit_url[1]}"
  279. event.message.delete
  280. event.send_temporary_message(temp_message, 15)
  281. member.dm(message)
  282. elsif event.message.reacted_with(Emoji::CROSS).count > 1
  283. event.message.delete
  284. end
  285. end
  286. end
  287. # This will trigger on every reaction is removed in discord
  288. bot.reaction_remove do |event|
  289. end
  290. # This will trigger when a member is updated
  291. bot.member_update do |event|
  292. end
  293. # This will trigger when anyone joins the server
  294. bot.member_join do |event|
  295. end
  296. # This will trigger when anyone leaves the server
  297. bot.member_leave do |event|
  298. end
  299. # This will trigger when anyone is banned from the server
  300. bot.user_ban do |event|
  301. end
  302. # This will trigger when anyone is un-banned from the server
  303. bot.user_unban do |event|
  304. end
  305. bot.run