main.rb 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. require 'yaml'
  2. require 'discordrb'
  3. require 'open-uri'
  4. require 'fileutils'
  5. require 'securerandom'
  6. require 'net/http'
  7. require 'json'
  8. require 'youtube-dl.rb'
  9. require 'sqlite3'
  10. db = SQLite3::Database.open 'tankbot.db'
  11. $settings = YAML.load(File.read "config.yaml")['settings']
  12. bot = Discordrb::Commands::CommandBot.new token: $settings['token'], prefix: $settings['prefix']
  13. def save_settings
  14. File.open("config.yaml", 'w') do |file|
  15. file.write(YAML.dump({'settings' => $settings}))
  16. end
  17. end
  18. bot.command(:reset) do |_event|
  19. _event.message.mentions.each do |user|
  20. user.on(_event.channel.server).nickname = nil
  21. end
  22. "Finished resetting"
  23. end
  24. bot.command(:reap, description: "Reaps images posted by the caller in the current channel.") do |_event, stop_id|
  25. next "No stop_id supplied" if stop_id == nil
  26. FileUtils::mkdir_p("/tmp/tankbot_images/")
  27. stop = false
  28. earliest_message_id = nil
  29. control_message = _event.send_message("Reaping images sent by #{_event.author.mention}. React to this message to stop.")
  30. until stop
  31. messages = _event.channel.history(100, earliest_message_id)
  32. messages.select{ |message| message.author === _event.author }.each do |message|
  33. puts "#{message.id}, #{stop_id}"
  34. if control_message.reactions? or message.id == stop_id
  35. stop = true
  36. break
  37. end
  38. message.attachments.select{ |attachment| attachment.image? }.each do |attachment|
  39. control_message.edit(control_message.content + "\nDownloading **#{attachment.filename}**... (#{message.timestamp.strftime("%F")})")
  40. open("/tmp/tankbot_images/#{attachment.id}-#{attachment.filename}", 'wb') do |file|
  41. file << open(attachment.url).read
  42. end
  43. end
  44. end
  45. stop = messages.length < 100
  46. earliest_message_id = messages.last.id
  47. end
  48. filename = "#{SecureRandom.uuid}.tar.gz"
  49. `tar -czf /var/www/scr/#{filename} /tmp/tankbot_images`
  50. FileUtils::rm_rf("/tmp/tankbot_images")
  51. control_message.edit("https://scr.tankernn.eu/#{filename}")
  52. end
  53. bot.command(:neko, description: "Requests (sometimes lewd) nekos.") do |_event, keyword|
  54. url = "https://nekos.life/api/v2/img/"
  55. options = ["meow", "woof", "tickle", "feed", "poke", "slap", "avatar", "waifu",
  56. "lizard", "pat", "kiss", "neko", "cuddle", "fox_girl", "hug"]
  57. if options.include? keyword then
  58. response = JSON.parse(Net::HTTP.get(URI("#{url}#{keyword}")))
  59. "Here's your lewds! °˖✧◝(⁰▿⁰)◜✧˖°\n#{response['url']}"
  60. else
  61. "No such tag. Please specify one of `#{options.join(", ")}`"
  62. end
  63. end
  64. bot.command(:lmgtfy, description: "Helps tech-illiterate people to enlightenment.") do |_event, *args|
  65. "http://lmgtfy.com/?s=d&q=#{args.join('+')}"
  66. end
  67. bot.command(:copypasta, description: "Cites the holy texts.") do |_event, keyword|
  68. pastafile = "copypastas.json"
  69. file = File.read pastafile
  70. pastas = JSON.parse file
  71. if pastas.include? keyword then
  72. pastas[keyword]
  73. else
  74. "No such pasta. Available pastas include `#{pastas.keys.join(", ")}`"
  75. end
  76. end
  77. class UserQueue
  78. def initialize(user)
  79. @user = user
  80. @songs = []
  81. end
  82. attr_accessor :user
  83. attr_accessor :songs
  84. end
  85. class FairQueue
  86. def initialize(voice_bot)
  87. @voice_bot = voice_bot
  88. @queues = []
  89. @now_playing = nil
  90. end
  91. def append(user, video)
  92. @queues.append UserQueue.new user unless @queues.any? { |queue| queue.user == user }
  93. @queues.select{ |queue| queue.user == user }.first.songs.append(video)
  94. end
  95. def queue
  96. queues = @queues.map{ |queue| queue.songs }
  97. target_length = queues.map{ |queue| queue.length }.max
  98. queues.map{ |queue| queue + (target_length - queue.length).times.collect{nil} }.transpose.flatten.compact
  99. end
  100. def play
  101. until @queues.empty?
  102. queue = @queues.shift
  103. @now_playing = queue.songs.shift
  104. # Rotate user to last place
  105. @queues.append queue unless queue.songs.empty?
  106. # Play song
  107. # song_log("Playing *#{video.title}*...")
  108. @voice_bot.play_file(@now_playing.filename)
  109. end
  110. @voice_bot.destroy
  111. end
  112. attr_accessor :now_playing
  113. end
  114. def format_title(video)
  115. total_seconds = video.information[:duration]
  116. seconds = total_seconds % 60
  117. minutes = (total_seconds / 60) % 60
  118. hours = total_seconds / (60 * 60)
  119. timestamp = format("%02d:%02d", minutes, seconds)
  120. timestamp = format("%02d:%s", hours, timestamp) if hours > 0
  121. "**#{video.information[:fulltitle]}** `[#{timestamp}]`"
  122. end
  123. fairqueues = Hash.new
  124. youtube_dl_options = {
  125. default_search: 'ytsearch',
  126. format: 'bestaudio',
  127. output: 'cache/%(title)s-%(id)s.%(ext)s'
  128. }
  129. bot.command(:play, description: "Plays 'music' of your choosing in your voice channel.") do |_event, *query|
  130. voice_bot = _event.voice
  131. unless voice_bot then
  132. channel = _event.user.voice_channel
  133. next "You're not in any voice channel!" unless channel
  134. voice_bot = bot.voice_connect(channel)
  135. voice_bot.volume = $settings['volume']
  136. fairqueues[_event.server] = FairQueue.new(voice_bot)
  137. end
  138. video = YoutubeDL.download query.join(' '), youtube_dl_options
  139. fairqueue = fairqueues[_event.server]
  140. fairqueue.append(_event.user, video)
  141. if voice_bot.playing? then
  142. "Added #{format_title(video)} to the queue."
  143. else
  144. Thread.new{fairqueue.play}
  145. "Started playing #{format_title(video)}"
  146. end
  147. end
  148. bot.command(:skip, description: "Expresses your dislike of the currently playing 'music'.") do |_event|
  149. _event.voice.stop_playing
  150. end
  151. bot.command(:np, description: "Shows what 'music' is currently playing.") do |_event|
  152. queue = fairqueues[_event.server]
  153. next "Nothing is playing." unless _event.voice
  154. format_title(queue.now_playing)
  155. end
  156. bot.command(:stop, description: "Puts an end to your misery.") do |_event|
  157. if _event.voice
  158. _event.voice.destroy
  159. "Stopped playing."
  160. else
  161. "Nothing is playing."
  162. end
  163. end
  164. bot.command(:queue, description: "Lists the impending torture.") do |_event|
  165. queue = fairqueues[_event.server]
  166. next "Nothing is playing." unless queue
  167. next "The queue is empty." if queue.queue.empty?
  168. queue.queue.each_with_index.map{|video, i| "#{i + 1}. #{format_title(video)}"}.join("\n")
  169. end
  170. bot.command(:volume, description: "Sets the severity of currently playing 'music'.") do |_event, volume|
  171. next "Nothing is playing." unless _event.voice
  172. $settings['volume'] = _event.voice.volume = [volume.to_f, 1.5].min
  173. save_settings
  174. ""
  175. end
  176. bot.command(:give, description: "Give a user some Good Boy Points (GBP).") do |_event|
  177. receiver = _event.message.mentions[0]
  178. sender = _event.author
  179. next 'You cannot give GBP to yourself.' if sender.id == receiver.id
  180. result = db.query "SELECT remaining FROM gbp WHERE user_id=?", sender.id
  181. first_result = result.next
  182. if first_result
  183. remaining = first_result[0]
  184. else
  185. remaining = $settings['gbp_per_week']
  186. db.execute "INSERT INTO gbp VALUES (?, ?, ?)", sender.id, 0, $settings['gbp_per_week']
  187. end
  188. next 'No GBP left to give this week.' unless remaining > 0
  189. result = db.query "SELECT received FROM gbp WHERE user_id=?", receiver.id
  190. first_result = result.next
  191. if first_result
  192. received = first_result[0]
  193. else
  194. received = 0
  195. db.execute "INSERT INTO gbp VALUES (?, ?, ?)", receiver.id, 0, $settings['gbp_per_week']
  196. end
  197. db.execute "UPDATE gbp SET received = ? WHERE user_id = ?", received + 1, receiver.id
  198. db.execute "UPDATE gbp SET remaining = ? WHERE user_id = ?", remaining - 1, sender.id
  199. "#{sender.mention} gave 1 GBP to #{receiver.mention}."
  200. end
  201. bot.command(:leaderboard, description: "Show who's got the most Good Boy Points (GBP).") do |_event|
  202. results = db.query "SELECT user_id, received FROM gbp ORDER BY received DESC"
  203. "**Good Boy Points (GBP) Leaderboard**\n" + results.each_with_index.map{|row, i| "#{i + 1}. #{_event.server.member(row[0]).nick} - #{row[1]}"}.join("\n")
  204. end
  205. bot.voice_state_update() do |_event|
  206. user = _event.user
  207. channel = _event.channel
  208. if channel.id == $settings['naughty_corner']
  209. # Deduct GBP
  210. result = db.query "SELECT received FROM gbp WHERE user_id=?", user.id
  211. first_result = result.next
  212. if first_result
  213. received = first_result[0]
  214. else
  215. received = 0
  216. db.execute "INSERT INTO gbp VALUES (?, ?, ?)", receiver.id, 0, $settings['gbp_per_week']
  217. end
  218. next unless received > 0
  219. db.execute "UPDATE gbp SET received = ? WHERE user_id = ?", received - 1, user.id
  220. end
  221. end
  222. bot.run