bot.rb 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. )
  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)
  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" * 100) : 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. # Record post lengths and count
  84. user = User.find(author.id)
  85. if msg_length >= 40
  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. else
  90. user.update(
  91. short_post_length: user.short_post_length + msg_length,
  92. short_post_count: user.short_post_count + 1
  93. )
  94. end
  95. end
  96. end
  97. # This will trigger when a dm is sent to the bot from a user
  98. bot.pm do |event|
  99. # Save the message contents
  100. content = event.message.content
  101. # Attempt to match a command
  102. cmd = /^pkmn-(\w+)/.match(content)
  103. # Check for a standard command
  104. if cmd
  105. # Search for the corresponding command
  106. commands = BaseCommand.descendants.filter{ |bc| bc.restricted_to == :pm }
  107. command = commands.detect{ |c| c.name == cmd[1].downcase.to_sym }
  108. # Call the command, and reply with its results
  109. reply = command&.call(content, event)
  110. BotController.reply(bot, event, reply)
  111. end
  112. end
  113. # This will trigger when any reaction is added in discord
  114. bot.reaction_add do |event|
  115. app_form = event.message.embeds.first
  116. if Team.find_by(channel: event.channel.id)
  117. reactions = event.message.reactions
  118. event.message.pin if reactions[Emoji::PIN]&.count.to_i > 0
  119. end
  120. # Only do logic if this is an embed
  121. break unless app_form
  122. # Find the appropriate app type, and process
  123. app = ApplicationForm.descendants.detect{ |af| af.name == app_form.author&.name }
  124. carousel = Carousel.find_by(message_id: event.message.id)
  125. reply = if app then app&.call(event)
  126. elsif carousel then carousel.navigate(event)
  127. end
  128. if reply
  129. BotController.reply(bot, event, reply)
  130. #elsif event.message&.reactions[Emoji::CROSS]&.count
  131. #crosses = event.message.reacted_with(Emoji::CROSS)
  132. #crosses.each do |cross|
  133. #member = event.server.member(cross.id)
  134. #event.message.delete unless member.current_bot?
  135. #end
  136. end
  137. end
  138. # This will trigger when any reaction is removed in discord
  139. bot.reaction_remove do |event|
  140. end
  141. # This will trigger when a member is updated
  142. bot.member_update do |event|
  143. end
  144. # This will trigger when anyone joins the server
  145. bot.member_join do |event|
  146. unless User.find_by(id: event.user.id)
  147. usr = User.create(id: event.user.id)
  148. usr.make_stats
  149. end
  150. end
  151. # This will trigger when anyone leaves the server
  152. bot.member_leave do |event|
  153. updated = []
  154. fields = []
  155. chars = Character.where(user_id: event.user.id)
  156. roles = event.roles
  157. roles = roles.map{ |r| "<@#{r}>" } if roles
  158. chars.each do |char|
  159. unless char.active == 'NPC'
  160. char.update(active: 'Deleted')
  161. char.reload
  162. end
  163. ct = CharTeam.find_by(char_id: char.id)
  164. ct.update(active: false) if ct
  165. t = Team.find(ct.team_id) if ct
  166. bot.send_message(t.channel, "#{char.name} has left the server", false, nil) if t
  167. updated.push("#{char.name}, #{t.name} -- #{char.active}") if t
  168. updated.push("#{char.name}, no team data -- #{char.active}") if t.nil?
  169. end
  170. fields.push({
  171. name: "```Flagging Guild Members......```",
  172. value: updated.join("\n")
  173. }) unless updated.empty?
  174. fields.push({
  175. name: "User's Roles",
  176. value: roles.join(", ")
  177. }) unless roles.empty?
  178. embed = Embed.new(
  179. title: "I've lost track of a user!",
  180. description: "It seems #{event.member.mention}, (#{event.user.username}) has left the server!",
  181. fields: fields
  182. )
  183. # production channel
  184. bot.send_message(588464466048581632, "", false, embed)
  185. # development channel
  186. #bot.send_message(594244240020865035, "", false, embed)
  187. end
  188. # This will trigger when anyone is banned from the server
  189. bot.user_ban do |event|
  190. updated = []
  191. fields = []
  192. chars = Character.where(user_id: event.user.id)
  193. roles = event.roles
  194. roles = roles.map{ |r| "<@#{r}>" } if roles
  195. chars.each do |char|
  196. unless char.active == 'NPC'
  197. char.update(active: 'Deleted')
  198. char.reload
  199. end
  200. ct = CharTeam.find_by(char_id: char.id)
  201. ct.update(active: false) if ct
  202. t = Team.find(ct.team_id) if ct
  203. updated.push("#{char.name}, #{t.name} -- #{char.active}") if t
  204. updated.push("#{char.name}, no team data -- #{char.active}") if t.nil?
  205. end
  206. fields.push({
  207. name: "```Flagging Guild Members......```",
  208. value: updated.join("\n")
  209. }) unless updated.empty?
  210. fields.push({
  211. name: "User's Roles",
  212. value: roles.join(", ")
  213. }) unless roles.empty?
  214. embed = Embed.new(
  215. title: "A User was forced to leave!",
  216. description: "It seems #{event.member.mention}, (#{event.user.username}) has been banned from the server!",
  217. fields: fields
  218. )
  219. # production channel
  220. bot.send_message(588464466048581632, "", false, embed)
  221. end
  222. # This will trigger when anyone is un-banned from the server
  223. bot.user_unban do |event|
  224. end
  225. bot.run