bot.rb 7.7 KB

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