Skip to content

Instantly share code, notes, and snippets.

@DataKinds
Last active June 15, 2016 08:50
Show Gist options
  • Save DataKinds/f159a250ceeede29c395b5af73cfdf1d to your computer and use it in GitHub Desktop.
Save DataKinds/f159a250ceeede29c395b5af73cfdf1d to your computer and use it in GitHub Desktop.
just my lil discord bot
module CodeRunner
def self.runCode(code, interpreter)
badKeywords = ["\`", "popen", "gets", "STDIN", "interact", "input", "system", "File", "file", "IO", "eval", "exec", "open", "write", "read", "Socket"]
malicious = false
badKeywords.each do |word|
if code.include? word then
malicious = true
end
end
if malicious then
output = "Hey, calm down there."
else
puts "running #{code} as #{interpreter}"
output = ""
File.open("tempCode", "w") { |file| file.write(code) }
thread = Thread.new {output = `#{interpreter} tempCode`}
thread.join 10
if output.length == 0 then
output = "Code either took longer than 10 seconds to run or produced no output."
elsif output.split(?\n).length > 5
output = output.split(?\n)[0..5].push("...").join(?\n)
end
end
#File.delete("tempCode")
return output
end
def self.runRuby(code)
return runCode(code, "ruby")
end
def self.runHaskell(code)
return runCode(code, "runghc")
end
end
#{
#word object:
# => uses words as keys
# => => word frequency
#people array:
# => uses peoples' names as keys
# => => times talked
# => => xp (controlled by giveXP)
# => => level (controlled by giveXP)
# => =>
# => =>
#}
require "discordrb"
require "json"
require "open-uri"
require_relative "coderunner.rb"
#defines $MY_TEXT_CHANNEL, $DISCORD_SECRET, and $DISCORD_APP_ID
require_relative "secrets.rb"
$congratulations = ["Hell yeah", "Good job", "Congratulations", "Keep up the good work", "XD", "Time to kill yourself", "You're special", "What are you, freakin gay", "Hey, that's pretty good"]
$bot = Discordrb::Bot.new token: $DISCORD_SECRET, application_id: $DISCORD_APP_ID
if !File.exist?("discordStatBot.json")
File.open("discordStatBot.json", "w") { |file| file.write('{"words": {}, "people": {}}') }
end
$stats = JSON.parse(File.read("discordStatBot.json"))
puts $stats
$floodProtectionObject = {}
$floodConvictionCounts = {}
def help()
$bot.send_message($MY_TEXT_CHANNEL, File.read("helpMessage"))
end
def sortKeysByAttribute(array, attribute)
unsortedArray = []
array.keys.each do |key|
unsortedArray.push({"key" => key, attribute => array[key][attribute]})
end
return unsortedArray.sort_by{ |keyWithAttribute| keyWithAttribute[attribute] }
end
def xpToLevel(xp)
(Math.sqrt(xp+16) * 0.25).floor
end
def levelToXP(level)
(((4 * level) ** 2) - 16).floor
end
def giveXP(person, amount)
$stats["people"][person]["xp"] = $stats["people"][person]["xp"] + amount
if xpToLevel($stats["people"][person]["xp"]) > $stats["people"][person]["level"]
$stats["people"][person]["level"] = xpToLevel($stats["people"][person]["xp"])
$bot.send_message($MY_TEXT_CHANNEL, "#{$congratulations.sample}! **#{$stats["people"][person]["nick"]}** just reached level #{$stats["people"][person]["level"]}.")
end
end
def removeXP(person, amount)
$stats["people"][person]["xp"] = $stats["people"][person]["xp"] - amount
if $stats["people"][person]["xp"] < 0
$stats["people"][person]["xp"] = 0
end
$stats["people"][person]["level"] = xpToLevel($stats["people"][person]["xp"])
$bot.send_message($MY_TEXT_CHANNEL, "#{$congratulations.sample}! **#{$stats["people"][person]["nick"]}** just lost #{amount} XP.")
end
#track message stats
$bot.message do |event|
#STATS FOR !WORDS
normalWordArray = event.content.downcase.gsub(/[^a-z0-9\s]/i, "").split(" ")
normalWordArray.each do |word|
if !$stats["words"].key?(word)
$stats["words"][word] = {"amount" => 1}
else
$stats["words"][word]["amount"] = $stats["words"][word]["amount"] + 1
end
end
#STATS FOR !PEOPLE
if !$stats["people"].key?("#{event.author.id}")
$stats["people"]["#{event.author.id}"] = {"timesSpoken" => 1, "nick" => event.author.display_name, "xp" => 0, "level" => 1}
event.respond "Welcome to the channel #{event.author.display_name}! You just reached **Level 1**. Type !xp to learn more."
else
#update the nickname just in case they changed it
$stats["people"]["#{event.author.id}"]["nick"] = event.author.display_name
$stats["people"]["#{event.author.id}"]["timesSpoken"] = $stats["people"]["#{event.author.id}"]["timesSpoken"] + 1
giveXP("#{event.author.id}", 10)
end
#FLOOD PROTECTION
#stores the timestamps for the last 3 messages, if they're too close
#then it takes 150 XP
if !$floodProtectionObject.key?("#{event.author.id}")
$floodProtectionObject["#{event.author.id}"] = []
end
if !$floodConvictionCounts.key?("#{event.author.id}")
$floodConvictionCounts["#{event.author.id}"] = 0
end
if event.author.display_name == "Aearnus"
$floodConvictionCounts["#{event.author.id}"] = -1000
end
$floodProtectionObject["#{event.author.id}"].push event.timestamp
if $floodProtectionObject["#{event.author.id}"].length > 4
$floodProtectionObject["#{event.author.id}"].shift
if ($floodProtectionObject["#{event.author.id}"][-1] - $floodProtectionObject["#{event.author.id}"][0]) < 5
event.respond("#{event.author.display_name}, stop flooding the chat.")
removeXP("#{event.author.id}", 150)
$floodConvictionCounts["#{event.author.id}"] = $floodConvictionCounts["#{event.author.id}"] + 1
end
end
#if a player is convicted 3 times, they are kicked and have to be reinvited
if $floodConvictionCounts["#{event.author.id}"] >= 3
event.server.kick(event.author)
$floodConvictionCounts["#{event.author.id}"] = 0
end
#SAVE STATS
File.open("discordStatBot.json", "w") { |file| file.write(JSON.generate($stats)) }
end
$bot.message(start_with: "!help") do |event|
help
end
$bot.message(start_with: "!words") do |event|
sortedWordArray = sortKeysByAttribute($stats["words"], "amount").reverse
outMessage = "**Top words used:**\n"
amountOfWords = 10
sortedWordArray.each do |wordWithAmount|
outMessage += "#{wordWithAmount["key"]}: #{wordWithAmount["amount"]} times\n"
amountOfWords -= 1
if amountOfWords <= 0
break
end
end
event.respond outMessage
end
$bot.message(start_with: "!people") do |event|
sortedPeopleArray = sortKeysByAttribute($stats["people"], "timesSpoken").reverse
outMessage = "**Most active people:**\n"
amountOfPeople = 10
sortedPeopleArray.each do |wordWithAmount|
personName = $stats["people"]["#{wordWithAmount['key']}"]["nick"]
personLevel = $stats["people"]["#{wordWithAmount['key']}"]["level"]
personXP = $stats["people"]["#{wordWithAmount['key']}"]["xp"]
outMessage += "#{personName}: Spoken #{wordWithAmount["timesSpoken"]} times, level #{personLevel} with #{personXP} XP.\n"
amountOfPeople -= 1
if amountOfPeople <= 0
break
end
end
event.respond outMessage
end
$bot.message(start_with: "!xp") do |event|
authorName = event.author.display_name
authorXP = $stats["people"]["#{event.author.id}"]["xp"]
authorLevel = $stats["people"]["#{event.author.id}"]["level"]
neededXP = levelToXP(authorLevel + 1)
event.respond "#{authorName}, you have #{authorXP} XP out of #{neededXP} XP and are level #{authorLevel}.\nXP can be gained by chatting, talking in voice channels, and other things."
end
$bot.message(start_with: "!ruby ") do |event|
event.respond CodeRunner::runRuby(event.content[6..-1])
end
$bot.message(start_with: "!haskell ") do |event|
event.respond CodeRunner::runHaskell(event.content[9..-1])
end
$bot.message(start_with: "!coin") do |event|
event.respond "#{%w(Heads. Tails.).sample}"
end
$bot.message(start_with: "!roll ") do |event|
diceNumbers = event.content.split(" ")[1..-1].map(&:to_i)
outDice = []
diceNumbers.each do |currentDie|
outDice.push rand(1..currentDie)
end
event.respond outDice.join(" ")
end
$bot.message(start_with: "!rpic ") do |event|
#http://stackoverflow.com/questions/4581075/how-make-a-http-request-using-ruby-on-rails
#http://stackoverflow.com/questions/6768238/download-an-image-from-a-url
subreddit = event.content.split(" ")[-1]
response = open("http://www.reddit.com/r/#{subreddit}/hot.json?count=100", {"User-Agent" => "a discord bot that sends pics (by /u/crazym4n)"}).read
subredditListing = JSON.parse(response)
imageUrls = []
subredditListing["data"]["children"].each do |post|
singleUrl = post["data"]["url"]
if singleUrl =~ /\.jpg|\.jpeg|\.bmp|\.png/
imageUrls.push(singleUrl)
end
end
if imageUrls.length > 0
cuteImageUrl = imageUrls.sample
puts "downloading #{cuteImageUrl}"
fileType = cuteImageUrl.split(".")[-1]
fileName = "cute.#{fileType}"
File.open(fileName, "wb") do |file|
#have to disable ssl verification because awwni.me's
#cert is fucking expired ofc
file.write open(cuteImageUrl, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read
end
File.open(fileName, "rb") do |file|
event.channel.send_file(file)
end
else
event.respond "Sorry! No images found."
end
end
$bot.run
Hello! I'm a bot that tracks the statistics of this channel. I also do flood and spam protection. If you send 4 messages within 5 seconds, you will have XP taken and be given a warning. 3 warnings automatically kicks you. I expose the following commands (all preceeded by a !):
`help`: Display this message.
`words`: Display the top 10 most used words in this channel.
`people`: Display the top 10 most active people in this channel.
`xp`: Display your level and various information about it.
`ruby`: Run a ruby program.
`haskell`: Run a haskell program.
`coin`: Flip a coin.
`roll`: Say a list of numbers after it, and it will roll dice with those numbers as max rolls.
`rpic`: Send an image from a subreddit you specify.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment