bot.rb 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. require 'bundler'
  2. require 'erb'
  3. require 'yaml'
  4. require 'json'
  5. require 'terminal-table'
  6. require 'rmagick'
  7. require 'down'
  8. BOT_ENV = ENV.fetch('BOT_ENV') { 'development' }
  9. Bundler.require(:default, BOT_ENV)
  10. require 'active_record'
  11. # Constants: such as roles and colors and regexes
  12. DISCORD = "#36393f"
  13. ERROR = "#a41e1f"
  14. UID = /<@\!?([0-9]+)>/
  15. URL = /https?:\/\/[\S]+/
  16. # ---
  17. Dotenv.load if BOT_ENV != 'production'
  18. db_yml = File.open('config/database.yml') do |erb|
  19. ERB.new(erb.read).result
  20. end
  21. db_config = YAML.safe_load(db_yml)[BOT_ENV]
  22. ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
  23. ActiveRecord::Base.establish_connection(
  24. adapter: 'postgresql',
  25. host: db_config.fetch('host') { 'localhost' },
  26. database: db_config['database'],
  27. user: db_config['user'],
  28. password: db_config['password']
  29. )
  30. Dir['app/**/*.rb'].each { |f| require File.join(File.expand_path(__dir__), f) }
  31. Dir['./lib/*.rb'].each { |f| require f }
  32. token = ENV['DISCORD_BOT_TOKEN']
  33. bot = Discordrb::Bot.new(token: token)
  34. #--
  35. # This will trigger on every message sent in discord
  36. bot.message do |event|
  37. # Break if not in a server.. for some reason pms trigger here
  38. break if event.server.nil?
  39. # Save the message contents, and author
  40. content = event.message.content
  41. author = event.author
  42. # Attempt to match a command
  43. cmd = /^pkmn-(\w+)/.match(content)
  44. # Check for a standard command
  45. if cmd
  46. # Search for the corresponding command
  47. commands = BaseCommand.descendants.filter{ |bc| bc.restricted_to == nil }
  48. command = commands.detect{ |c| c.name == cmd[1].downcase.to_sym }
  49. # Call the command, and reply with its results
  50. reply = command&.call(content, event)
  51. BotController.reply(bot, event, reply)
  52. # Check for a form that needs to be reacted to
  53. elsif author.id == ENV['WEBHOOK'].to_i
  54. # Save the app, and check app if character
  55. app = event.message.embeds.first
  56. if app.author.name == 'Character Application'
  57. case reply = CharacterController.authenticate_char_app(event)
  58. when Embed
  59. BotController.application_react(event)
  60. BotController.reply(bot, event, reply)
  61. when true
  62. BotController.application_react(event)
  63. when false
  64. BotController.unauthorized_char_app(bot, event, member)
  65. end
  66. else
  67. BotController.application_react(event)
  68. end
  69. # Check for a clear command
  70. elsif ENV['CLEAR_CH'].include?(event.channel.id.to_s) && content.match(/^clear\schat$/i)
  71. msgs = event.channel.history(50).reject{ |m| m.author.webhook? }
  72. event.channel.delete_messages(msgs)
  73. # Check for no exp channels
  74. elsif ENV['NO_EXP_CH'].include?(event.channel.id.to_s)
  75. # Do nothing
  76. # Apply experience to non-bot users
  77. elsif !author.bot_account? && !author.webhook?
  78. # Replace any urls with 150 'x' chars
  79. message = URL.match(content) ? content.gsub(URL, "x" * 150) : content
  80. # Add 40 to the message length if there's a file
  81. msg_length = event.message.attachments.map(&:filename).count > 0 ?
  82. 40 + message.length : message.length
  83. if msg_length >= 40
  84. # Wait until now to find user, so DB isn't touched for every message
  85. user = User.find(author.id)
  86. # Update User and reply with image, if there is one
  87. reply = user.update_xp(msg_length, author)
  88. bot.send_file(event.channel.id, File.open(reply, 'r')) if reply
  89. end
  90. end
  91. end
  92. # This will trigger when a dm is sent to the bot from a user
  93. bot.pm do |event|
  94. # Save the message contents
  95. content = event.message.content
  96. # Attempt to match a command
  97. cmd = /^pkmn-(\w+)/.match(content)
  98. # Check for a standard command
  99. if cmd
  100. # Search for the corresponding command
  101. commands = BaseCommand.descendants.filter{ |bc| bc.restricted_to == :pm }
  102. command = commands.detect{ |c| c.name == cmd[1].downcase.to_sym }
  103. # Call the command, and reply with its results
  104. reply = command&.call(content, event)
  105. BotController.reply(bot, event, reply)
  106. end
  107. end
  108. # This will trigger when any reaction is added in discord
  109. bot.reaction_add do |event|
  110. app_form = event.message.embeds.first
  111. if Team.find_by(channel: event.channel.id)
  112. reactions = event.message.reactions
  113. event.message.pin if reactions[Emoji::PIN]&.count.to_i > 0
  114. end
  115. # Only do logic if this is an embed
  116. break unless app_form
  117. # Find the appropriate app type, and process
  118. app = ApplicationForm.descendants.detect{ |af| af.name == app_form.author&.name }
  119. carousel = Carousel.find_by(message_id: event.message.id)
  120. reply = if app then app&.call(event)
  121. elsif carousel then carousel.navigate(event)
  122. end
  123. if reply
  124. BotController.reply(bot, event, reply)
  125. #elsif event.message&.reactions[Emoji::CROSS]&.count
  126. #crosses = event.message.reacted_with(Emoji::CROSS)
  127. #crosses.each do |cross|
  128. #member = event.server.member(cross.id)
  129. #event.message.delete unless member.current_bot?
  130. #end
  131. end
  132. end
  133. # This will trigger when any reaction is removed in discord
  134. bot.reaction_remove do |event|
  135. end
  136. # This will trigger when a member is updated
  137. bot.member_update do |event|
  138. end
  139. # This will trigger when anyone joins the server
  140. bot.member_join do |event|
  141. unless User.find_by(id: event.user.id)
  142. usr = User.create(id: event.user.id)
  143. usr.make_stats
  144. end
  145. end
  146. # This will trigger when anyone leaves the server
  147. bot.member_leave do |event|
  148. updated = []
  149. fields = []
  150. chars = Character.where(user_id: event.user.id)
  151. roles = event.roles
  152. roles = roles.map{ |r| "<@#{r}>" } if roles
  153. chars.each do |char|
  154. unless char.active == 'NPC'
  155. char.update(active: 'Deleted')
  156. char.reload
  157. end
  158. ct = CharTeam.find_by(char_id: char.id)
  159. ct.update(active: false) if ct
  160. t = Team.find(ct.team_id) if ct
  161. bot.send_message(t.channel, "#{char.name} has left the server", false, nil) if t
  162. updated.push("#{char.name}, #{t.name} -- #{char.active}") if t
  163. updated.push("#{char.name}, no team data -- #{char.active}") if t.nil?
  164. end
  165. fields.push({
  166. name: "```Flagging Guild Members......```",
  167. value: updated.join("\n")
  168. }) unless updated.empty?
  169. fields.push({
  170. name: "User's Roles",
  171. value: roles.join(", ")
  172. }) unless roles.empty?
  173. embed = Embed.new(
  174. title: "I've lost track of a user!",
  175. description: "It seems #{event.member.mention}, (#{event.user.username}) has left the server!",
  176. fields: fields
  177. )
  178. # production channel
  179. bot.send_message(588464466048581632, "", false, embed)
  180. # development channel
  181. #bot.send_message(594244240020865035, "", false, embed)
  182. end
  183. # This will trigger when anyone is banned from the server
  184. bot.user_ban do |event|
  185. updated = []
  186. fields = []
  187. chars = Character.where(user_id: event.user.id)
  188. roles = event.roles
  189. roles = roles.map{ |r| "<@#{r}>" } if roles
  190. chars.each do |char|
  191. unless char.active == 'NPC'
  192. char.update(active: 'Deleted')
  193. char.reload
  194. end
  195. ct = CharTeam.find_by(char_id: char.id)
  196. ct.update(active: false) if ct
  197. t = Team.find(ct.team_id) if ct
  198. updated.push("#{char.name}, #{t.name} -- #{char.active}") if t
  199. updated.push("#{char.name}, no team data -- #{char.active}") if t.nil?
  200. end
  201. fields.push({
  202. name: "```Flagging Guild Members......```",
  203. value: updated.join("\n")
  204. }) unless updated.empty?
  205. fields.push({
  206. name: "User's Roles",
  207. value: roles.join(", ")
  208. }) unless roles.empty?
  209. embed = Embed.new(
  210. title: "A User was forced to leave!",
  211. description: "It seems #{event.member.mention}, (#{event.user.username}) has been banned from the server!",
  212. fields: fields
  213. )
  214. # production channel
  215. bot.send_message(588464466048581632, "", false, embed)
  216. end
  217. # This will trigger when anyone is un-banned from the server
  218. bot.user_unban do |event|
  219. end
  220. bot.run