Initial commit of botqui (ruby)

This commit is contained in:
Monqui 2025-07-16 00:27:46 +00:00
parent bf08959178
commit 55b712b4ad
7 changed files with 134755 additions and 0 deletions

648
botqui.rb Normal file
View file

@ -0,0 +1,648 @@
require 'cinch'
require 'json'
require 'time'
require 'chronic'
require 'open-uri'
require 'nokogiri/class_resolver'
require 'nokogiri'
require 'uri'
require 'net/http'
require 'json'
require 'htmlentities'
load "trivia_master.rb"
def fix_nick(n)
return n[0] + "\uFEFF" + n[1,n.length]
end
def human_time(time)
out_str = ""
if time > (60*60*24) then #days
out_str = (time/(60*60*24)).to_i.to_s + " days"
elsif time > (60*60) then #hours
out_str = (time/(60*60)).to_i.to_s + " hours"
elsif time > (60) then #minutes
out_str = (time/(60)).to_i.to_s + " minutes"
else #seconds
out_str = time.to_s + " seconds"
end
return out_str
end
def get_data(f)
data = JSON.parse(File.read("data.bd"))
if data[f] == nil then
return {}
else
return data[f]
end
end
def set_data(f, v)
data = JSON.parse(File.read("data.bd"))
data[f] = v
File.write("data.bd", JSON.generate(data))
end
bot = Cinch::Bot.new do
pounces = []
seens = {}
who = ""
sentence = ""
who_working = false
last_saids = {}
trivData = {}
isTriv = false
trev_running = false
trev_answers_running = false
trev_guesses = {}
real_answer = ""
countdown_running = false
configure do |c|
c.server = "irc.libera.chat"
c.user = "Monqui"
c.password = "tbmsn"
c.nick = "botqu9001"
c.delay_joins = 15
c.channels = ["#sconesandcream fatjoints", "#sconetv", "#bakedbeans"]
c.timeouts.connect = 30
end
on :message, ".bots" do |m|
m.reply "RePoRTiNg iN 🐵! [ruby] Lmao!"
end
on :message, /^!thank.*/ do |m|
links = [
"https://i.imgur.com/P2jEPy7.png",
"https://i.imgur.com/q1IzteE.jpg",
"https://i.imgur.com/BM1V3Um.png",
"https://i.imgur.com/RuLveTf.jpg",
"https://i.imgur.com/ramcAYX.png"
]
m.reply links.sample
end
on :message, "!nonce" do |m|
m.reply "French people."
end
on :message, "!nipples" do |m|
m.reply "(.)(.) ooooo"
end
on :message, "!rub" do |m|
m.reply "/me mmmmmmmmmmmmmmmm's"
end
on :message, "!quote" do |m|
q = Quoter.new()
m.reply(q.get_quote)
end
on :message, /^!quote (.+)$/ do |m, quote|
q = Quoter.new()
q.save_quote(quote)
m.reply "K."
end
on :message, /^!delquote (.*)$/ do |m, id|
q = Quoter.new()
m.reply(q.del_quote(id))
end
on :channel, /^!apod$/ do |m|
doc = Nokogiri::HTML.parse(URI.open('https://apod.nasa.gov/apod/astropix.html'))
img = doc.xpath("/html/body/center[1]/p[2]/a/img")
uri = "https://apod.nasa.gov/" + img.attr("src")
name = "\u0002" + doc.xpath("/html/body/center[2]/b[1]").inner_html.strip + "\u000F"
m.reply name + ": " + uri
end
on :channel, /^!moon$/ do |m|
doc = Nokogiri::HTML.parse(URI.open('https://www.timeanddate.com/moon/phases/'))
img = doc.xpath("//*[@id='cur-moon-percent']")
m.reply img.inner_html.to_s
end
on :channel, /^!google (.*)$/ do |m, terms|
google_url = "https://www.google.com/search?q=" + terms.gsub(' ', '%20')
m.reply google_url
end
on :message, /([\.\d]+)lb/i do |m, val|
if m.user.nick.to_s != " Ballbag" then
m.reply val.to_s + "lb = " + (val.to_f * 0.454).round(2).to_s + "kg"
end
end
on :message, /([\.\d]+)kg/i do |m, val|
if m.user.nick.to_s != "Ballbag" then
m.reply val.to_s + "kg = " + (val.to_f * 2.2046).round(2).to_s + "lb"
end
end
on :message, /^!roll ([0-9]+)d([0-9]+)$/ do |m, num, max|
if num.to_i > 30 or max.to_i > 100 then
m.reply "bro be real."
return
end
count = 0
value = 0
vals = []
while count < num.to_i do
roll = rand(1..max.to_i)
vals.push(roll)
value = value + roll
count = count + 1
end
m.reply "#{m.user.nick} rolled " + vals.join('+') + " = " + value.to_s
end
on :message, /.* bear .*/ do |m|
m.reply "https://33.media.tumblr.com/fe1446dc5f065b3685fe63c51121fc4f/tumblr_mhhadl964P1rgfo8qo1_r2_400.gif"
end
on :message, /.*trinity.*/i do |m|
m.reply "https://tenor.com/view/trinity-taylor-drag-race-sip-cocktail-drama-gif-9086550"
end
on :message, "hello" do |m|
m.reply "Hello, #{m.user.nick}"
end
on :channel, /^!pounce (\S*) (.*)$/ do |m, t, message|
save_msg = {"source" => m.user.nick, "target" => t, "message" => '', 'time' => ''}
if message.index("|") != nil then
msg_parts = message.split(' | ')
save_msg["message"] = msg_parts[1]
triggerTime = Chronic.parse(msg_parts[0])
if triggerTime == nil then
save_msg["time"] = Time.now
else
save_msg["time"] = triggerTime
end
else
save_msg["time"] = Time.now
save_msg["message"] = message
end
# m.reply save_msg.inspect
pounces.push save_msg
# if !pounces[t.downcase].key?(m.user.nick) then
# pounces[t.downcase][m.user.nick] = save_msg
# else
# pounces[t.downcase][m.user.nick] = pounces[t.downcase][m.user.nick] + " | " + message
# end
m.reply "Will pounce " + t + " next time I see 'em."
end
on :channel, /^(.*)$/ do |m, val|
nick = m.user.nick
now = Time.now
remaining_pounces = []
out_msgs = {}
pounces.each do |p|
if nick == p["target"] and now > p["time"] then
out_msgs[nick] = p["source"] + " says " + p["message"] + "| " + out_msgs[nick].to_s
else
remaining_pounces.push p
end
end
out_msgs.each do |key, val|
m.reply "Hey " + key + ", " + val
end
# m.reply out_msgs.inspect
pounces = remaining_pounces
# if pounces.key?(nick.downcase) then
# pounces[nick.downcase].each_key{|i|
# m.reply "Hey " + nick + ", " + i.to_s + " says " + pounces[nick.downcase][i]
# }
# pounces.delete(nick.downcase)
end
on :message, /^!print (.*)$/ do |m, cmd|
m.reply cmd
end
on :channel, /^.*$/ do |m|
if m.message.slice(0, 2) != "s/" and m.message.slice(0, 1) != "!" and m.user.nick.to_s != "Ballbag" then
last_saids[m.user.nick] = m.message
end
end
on :channel, /^!britify(.*)$/ do |m, msg|
britese = ['oi', 'u wot', 'm8', 'mate', 'innit', 'bruv', 'fam', "ya kna wha 'm sayin"]
msg = msg.strip
orig = '?'
if msg == '' then
orig = last_saids[m.user.nick]
else
orig = msg
end
m.reply britese.sample + " " +
orig.gsub(/(o)/i, 'ou').
gsub(/(z)/i, 's').
gsub(/(ck)/i, 'que').
gsub(/( of)/i, 'o\'').
gsub(/(au)/i, 'o').
gsub(/(and)/i, 'an\'').
gsub(/(the)/i, 'tha').
gsub(/(he)/i, '\'e') +
" " + britese.sample
end
# on :channel, /^s\/(.*)\/(.*)\/? ?(.*) ?$/ do |m, f, r, n|
on :channel, /^s\/(.*)\/(.*)(\/[^\s]*)?(\s+([^\s]+))?\s*$/ do |m, f, r, o, n, x|
nick = m.user.nick
if n != nil and n.strip != "" then
nick = n.strip
end
ls = last_saids[nick]
re = /#{f}/
sedres = ls.gsub(re, r)
m.reply nick + " meant to say: " + sedres #s.try(last_saids[nick])
end
on :channel, /^!fc (.*)$/ do |m, val|
if m.user.nick.to_s != "Ballbag" then
m.reply ((val.to_f-32).to_f/1.8).to_s + "C"
end
end
on :channel, /^!cf (.*)$/ do |m, val|
if m.user.nick.to_s != "Ballbag" then
m.reply ((val.to_f * 1.8)+32).to_s + "F"
end
end
on :message, /(^|\s)(-?\d+)f($|\s)/i do |m, junk, val, other|
if m.user.nick.to_s != "Ballbag" then
m.reply val.to_s + "F = " + ((val.to_i-32).to_f/1.8).round(2).to_s + "C"
end
end
on :message, /(^|\s)(-?\d+)c($|\s)/i do |m, junk, val, other|
if m.user.nick.to_s != "Ballbag" then
m.reply val.to_s + "c = " + ((val.to_i * 1.8)+32).round(2).to_s + "F"
end
end
on :channel, /^!triv$/ do |m|
if isTriv == false then
tm = TriviaMaster.new()
trivData = tm.get_question()
# m.reply questionBits.inspect
isTriv = true
end
m.reply trivData['question'] + " " + trivData['answer_mask']
end
on :channel, /^!trivstop$/ do |m|
isTriv = false
m.reply "Stopping trivia."
end
on :channel, /^!trivhint$/ do |m|
new_mask = trivData['answer_mask']
starPositions = []
for i in 0 .. new_mask.length-1
if new_mask[i] == '*' then starPositions.push(i) end
end
if starPositions.length == 0 and isTriv == true then
isTriv = false
m.reply "HOLY TITS botqui WON"
if !data['trivias'].key?('botqui') then
data['trivias']['botqui'] = 0
end
data['trivias'][nick] = data['trivias'][nick] + 1
File.open("data.bd", 'w') { |file| file.write(JSON.generate(data)) }
m.reply "botqui currently has " + data['trivias']['botqui'].to_s + " wins."
else
revealPos = starPositions.sample
new_mask[revealPos] = trivData['answer'][revealPos]
trivData['answer_mask'] = new_mask
m.reply trivData['answer_mask']
end
end
on :channel, /^(.*)$/ do |m, val|
if isTriv then
val = val.downcase
if val.downcase == trivData['answer'].downcase then
m.reply "YOU WON HOLY SHIT - the answer was " + trivData['answer']
isTriv = false
nick = m.user.nick
if !data['trivias'].key?(nick) then
data['trivias'][nick] = 0
end
data['trivias'][nick] = data['trivias'][nick] + 1
File.open("data.bd", 'w') { |file| file.write(JSON.generate(data)) }
m.reply nick + " currently has " + data['trivias'][nick].to_s + " wins."
else
tmp_answer = trivData['answer'].downcase
new_mask = trivData['answer_mask']
found = false
for i in 0 .. [val.length, trivData['answer'].length].min
if val[i] == tmp_answer[i] and trivData['answer_mask'][i] == '*' then
found = true
new_mask[i] = trivData['answer'][i]
end
end
trivData['answer_mask'] = new_mask
if found then
m.reply new_mask + " (!trivstop to stop)"
end
end
end
end
on :channel, /^!imgur (.*)$/ do |m, tag|
m.reply "http://i.imgur.com/" + tag
end
on :channel, /^(.*)$/ do |m, last_said|
seens[m.user.nick.downcase] = {"message": last_said, "time": Time.now().to_i}
end
on :channel, /^!seen (.*$)/ do |m, nick_long|
nick = nick_long.strip
if !seens.key?(nick.downcase) then
m.reply "Haven't seen " + nick + " since last reboot..."
else
ht = human_time(Time.now().to_i - seens[nick.downcase][:time])
m.reply nick + " said \"" + seens[nick.downcase][:message] + "\" " + ht + " ago."
end
end
on :channel, /^!remind (.*)[\|:](.*)$/ do |m, time, event|
event = event.strip
triggerTime = Chronic.parse(time)
if triggerTime == nil then
m.reply "Iunno wtf time that is."
return
else
timeOut = triggerTime.to_i - Time.now().to_i
if timeOut <= 0 then
m.reply "YOU CAN'T CHANGE THE PAST, MAN!"
else
m.reply 'Will remind you to ' + event + ' in about ' + (timeOut).to_s + " secondsish."
sleep(timeOut)
m.reply m.user.nick.to_s + ": " + event
end
end
end
on :channel, /^!countdown$/ do |m|
if countdown_running == false then
countdown_running = true
start = 5
while start > 0 do
m.reply start
start = start - 1
sleep(1)
end
countdown_running = false
m.reply "GO!"
else
return
end
end
on :channel, /^!cd$/ do |m|
if countdown_running == false then
countdown_running = true
start = 5
while start > 0 do
m.reply start
start = start - 1
sleep(1)
end
countdown_running = false
m.reply "GO!"
end
end
on :channel, /^!trev$/ do |m|
if trev_running == true then
m.reply "PLAY THE GAME!"
else
trev_running = true
res = Net::HTTP.get_response(URI('https://opentdb.com/api.php?amount=1'))
if res.is_a?(Net::HTTPSuccess)
question = JSON.parse(res.body)
question_text = HTMLEntities.new.decode(question["results"][0]["question"])
answer = question["results"][0]["correct_answer"]
category = question["results"][0]["category"]
difficulty = question["results"][0]["difficulty"]
multiplier = 1
difficulty_tag = "H"
case difficulty
when "easy"
multiplier = 0.33
difficulty_tag = "E"
when "medium"
multiplier = 0.66
difficulty_tag = "M"
when "hard"
multiplier = 1.0
difficulty_tag = "H"
else
m.reply "wtf does " + difficulty + " mean??"
end
answers = question["results"][0]["incorrect_answers"]
answers.push(answer)
answers.shuffle!
m.reply "🖕👁️👄👁️🖕"
m.reply "[" + category + "] (\x02" + difficulty_tag + "\x02) " + question_text
out_str = ""
current_answer = "A"
max_answer = answers.max{|c1, c2| HTMLEntities.new.decode(c1).length <=> HTMLEntities.new.decode(c2).length}
max_length = max_answer.length + 2 # 2 for padding a bit...
padded_answers = answers.map{|v| v.ljust(max_length.to_i) }
answers_shown = 0
padded_answers.each do |a|
answers_shown = answers_shown + 1
if a.strip == answer.strip then
real_answer = current_answer.clone
end
out_str = out_str + "\x02" + current_answer + "\x0F) " + HTMLEntities.new.decode(a) + " "
if answers_shown % 2 == 0 then
out_str.strip!
out_str = out_str + "\n"
end
current_answer.next!
end
m.reply out_str.strip
m.reply "TRIVIA STARTING IN 5 SECONDS!"
sleep(5)
trev_answers_running = true
m.reply "You have 30 seconds! GO!"
start_time = Time.now.to_i
sleep(30)
m.reply "The answer was \x02" + real_answer + "\x0F) " + HTMLEntities.new.decode(answer) + "!"
someone_won = false
data = get_data('trevias')
trev_guesses.each do |g|
puts g.inspect
puts trev_guesses.inspect
if g[1] > 0 then
someone_won = true
nick = g[0]
time_took = g[1] - start_time
points = ((((30-time_took).to_f/30.0) * 100)*multiplier).floor.to_i
if !data.key?(nick) then
data[nick] = 0
end
data[nick] = data[nick] + points
m.reply nick + " got it and took " + time_took.to_s + "s! Wins " + points.to_s + " points! They have " + data[nick].to_s + " total points now!"
end
end
if someone_won == false then
m.reply "Looks like no one got it! Better luck next time!"
end
set_data('trevias', data)
trev_guesses = {}
trev_running = false
trev_answers_running = false
else
trev_running = false
trev_answers_running = false #just in case
m.reply "idk I failed to work I am sorry :("
end
end
end
on :channel, /^([abcd])$/i do |m, a|
if trev_answers_running == true then
nick = m.user.nick
# check if nick exists in hash... if not add it.
if trev_guesses.has_key? nick then
# m.reply "lmao u already guessed!"
else
if real_answer.downcase == a.downcase then
trev_guesses[nick] = Time.now.to_i
else
trev_guesses[nick] = 0
end
m.reply nick + " is locked in with " + a + "!"
end
end
end
on :channel, /^!trevscores$/ do |m|
scores = get_data('trevias')
scores = scores.sort_by { |name, s| s}
scores_str = ""
real_scores = []
scores.each do |e|
real_scores.unshift e
end
real_scores.each do |e|
scores_str = scores_str + "(" + fix_nick(e[0]) + ": " + e[1].to_s + ") "
end
m.reply scores_str
end
on :channel, /^!smoke$/ do |m|
m.reply " ( )/"
m.reply " )(/"
m.reply "_________________ ( /)"
m.reply "()__)____________)))))"
end
end
bot.start

1
data.bd Normal file
View file

@ -0,0 +1 @@
{"trevias":{"Monqui":6807,"Budrick":1259,"cr0sis":5355,"megasconed":620,"w2r5z":92}}

0
test Normal file
View file

94
triv_api.rb Normal file
View file

@ -0,0 +1,94 @@
require 'uri'
require 'net/http'
require 'json'
require 'cgi'
uri = URI('https://opentdb.com/api.php?amount=1')
res = Net::HTTP.get_response(uri)
trev_running = false
if res.is_a?(Net::HTTPSuccess)
question = JSON.parse(res.body)
puts question.inspect
difficulty = question["results"][0]["difficulty"]
puts difficulty
question_text = CGI.unescapeHTML(question["results"][0]["question"])
answer = question["results"][0]["correct_answer"]
puts answer
category = question["results"][0]["category"]
answers = question["results"][0]["incorrect_answers"]
answers.push(answer)
answers.shuffle!
# puts answers.inspect
puts "[" + category + "] " + question_text
out_str = ""
current_answer = "A"
real_answer = ""
max_answer = answers.max{|c1, c2| c1.length <=> c2.length}
max_length = max_answer.length + 2 # 2 for padding a bit...
puts "max_length.to_s = " + max_length.to_s
padded_answers = answers.map{|v| v.ljust(max_length.to_i) }
answers_shown = 0
padded_answers.each{ |a|
answers_shown = answers_shown + 1
if a == answer then
real_answer = current_answer.clone
end
out_str = out_str + current_answer + ") " + CGI.unescapeHTML(a) + " "
if answers_shown % 2 == 0 then
out_str.strip!
out_str = out_str + "\n"
end
current_answer.next!
}
puts real_answer
out_str.delete_suffix!(" |")
puts out_str.strip
puts "TRIVIA STARTING IN 5 SECONDS!"
puts Time.now.to_i.to_s + " start 5s timer..."
sleep(5)
trev_running = true
# puts Time.now.to_i.to_s + " OK ABOUT TO START??"
puts "You have 30 seconds! Go!"
# puts real_answer
puts Time.now.to_i.to_s + " start guess time"
sleep(30)
puts Time.now.to_i.to_s + " end time"
# Check the user answers
# Check their times, calc score and save it,
# Reset answer array, save.
trev_running = false
end
# have trigger for !trev to start. Flag something to flip playing to true.
# have trigger for guesses- matches on case insensitive ^ABCDTF$

6775
triv_questions.txt Normal file

File diff suppressed because it is too large Load diff

127201
triv_questions_new.txt Normal file

File diff suppressed because it is too large Load diff

36
trivia_master.rb Normal file
View file

@ -0,0 +1,36 @@
class TriviaMaster
def initialize()
@questions = []
File.open("triv_questions_new.txt").each_line{|line|
@questions.push(line)
}
end
def get_question()
q = @questions.sample
questionBits = q.split('*')
# m.reply questionBits.inspect
qData = {}
qData['question'] = questionBits[0].strip
qData['answer'] = questionBits[1].strip
# mask this somehow. answer "I did it" would become "* *** **"
mask = ""
questionBits[1].strip().each_char{|c|
# this is so ugly.
if c == '-' or c == '\'' or c == ':' or c == "," or c == "." or c == "!" or c == "?" or c == "%" or c == "&" then
mask = mask + c
elsif c == ' ' then
mask = mask + ' '
else
mask = mask + '*'
end
}
qData['answer_mask'] = mask
return qData
end
end