main.rb 7.4 KB

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