Skip to content

Instantly share code, notes, and snippets.

@FiXato
Forked from UnknownEntity634/weebots.rb
Last active December 16, 2015 09:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FiXato/5415091 to your computer and use it in GitHub Desktop.
Save FiXato/5415091 to your computer and use it in GitHub Desktop.
WeeChat script to parse BotServ Botlists to find out which bots are no longer in use.
#!/usr/bin/env ruby
# encoding: utf-8
#
# A WeeChat script to parse Anope's BotServ BOTLIST into a sorted list,
# so you can more easily find unassigned BotServ bots.
#
# Code by Unknown Entity (aka o_o aka Flaming Nutmeg) and Filip H.F. "FiXato" Slagter
# Based on the code by Unknown Entity
#
# Type /parsebotlist and go get some noms depending on bot list length while it collects info.
#
# TODO:
# - Store the server buffer, instead of requesting it for every request.
require 'fileutils'
def weechat_init
Weechat.register("weebots", "Unknown Entity & FiXato", "1.2", "GPL3", "Acquire /bs info on bots and parse it out", "", "")
Weechat.hook_signal("*,irc_in2_notice", "notice_cb", "")
Weechat.hook_signal("*,irc_in2_319", "cb_319", "") # Channel list from /whois nick
Weechat.hook_signal("*,irc_in2_317", "cb_317", "") # Idle time from /whois nick nick
hook = Weechat.hook_command("parsebotlist", "Parse botlist info retrieved after having done /bs botlist", "", "", "", "parsebotlist_cb", "")
# Check path option, if nonexistent create it
if Weechat.config_is_set_plugin('outpath') == 0
Weechat.config_set_plugin('outpath', File.expand_path(File.join('~', 'weebotslist')))
end
outpath = Weechat.config_get_plugin('outpath')
Weechat.print('', 'weebots Output path set to ' + outpath)
# Make the directory if it doesn't exist
FileUtils.mkdir_p(outpath) unless File.exist?(outpath)
# @bots = {:private => {}, :public => {}, :unknown => {}}
@bots = Hash.new{|h,k|h[k] = {}}
@bottype = :unknown
@current_botname = "1UNKNOWN"
return Weechat::WEECHAT_RC_OK
end
# grab only botserv notices we need
def notice_cb(data, signal, signal_data)
outpath = Weechat.config_get_plugin('outpath')
nick = Weechat.info_get("irc_nick_from_host", signal_data)
# fp = File.open(File.expand_path(File.join(outpath,'botlist')),'a+')
# fpd = File.open(File.expand_path(File.join(outpath,'botlistdetail')),'a+')
# Limit the split to 3 matches.
# The raw data has 2 colons in it; one at the very start and one at the start of the message proper.
# Anything after that is not to be counted.
#buffer = signal_data.split(":",3)[-1]
buffer = signal_data.split(":",3)[-1]
if (buffer && nick == 'BotServ') # Filter out stuff like server notices; we don't care about those here
#/bs botlist checking first
# Weechat.print("", "Buffer: #{buffer}")
if buffer.match(/Bot list:/)
@bottype = :public
# Weechat.print("", "=============Public!")
# Weechat.print("", "Current botname: #{@current_botname.inspect}")
# fp.puts('=== Public Bot List ===')
elsif md = buffer.match(/^\s{3}(\S+)\s+\((\S+)@(\S+)\)/)
@current_botname = md[1].gsub("\002",'').strip
File.open(File.expand_path(File.join(outpath,'names.txt')), "a+"){|f|f.puts "'#{@current_botname}'"}
@bots[@current_botname][:type] ||= @bottype
@bots[@current_botname][:nick] = @current_botname
@bots[@current_botname][:ident] = md[2]
@bots[@current_botname][:host] = md[3]
# fp.puts(botname)
elsif md = buffer.match(/Bots reserved to IRC operators:/)
# Weechat.print("", "=============Private!")
@bottype = :private
# fp.puts('=== Private Bot List ===')
# And now for /bs info checking
elsif md = buffer.match(/^Information for bot\s+(\S+):/)
@current_botname = md[1].gsub("\002",'').strip
File.open(File.expand_path(File.join(outpath,'names.txt')), "a+"){|f|f.puts "'#{@current_botname}'"}
# Weechat.print("", "Botinfo for '#{@current_botname.chars.map{|c|c}.inspect}' #{@current_botname.size}")
# fpd.print("Found #{botname}!")
elsif md = buffer.match(/^ Mask : (.*)/)
mask = md[1]
# Weechat.print("", "Mask for '#{@current_botname}': #{mask}")
@bots[@current_botname][:mask] = mask
# fpd.print("#{mask} ")
elsif md = buffer.match(/^ Real name : (.*)/)
gecos = md[1]
# Weechat.print("", "Gecos for '#{@current_botname}': #{gecos}")
@bots[@current_botname][:realname] = gecos
# fpd.print("(#{realname})")
elsif md = buffer.match(/^\s+Used on : (\d+) channel\(s\)/)
@bots[@current_botname][:channels] = [] if md[1].to_i > 0
elsif (/\s{1,2}(#\S+)+/.match(buffer))
# fpd.print ' => '
matches = buffer.scan(/\s{1,2}(#\S+)+/) do |m|
@bots[@current_botname][:channels] << m[0]
end
# fpd.puts("")
end
write_file(nil, nil)
end
# fp.close
# fpd.close
return Weechat::WEECHAT_RC_OK
end
# Now we need to grab raw /whois output; to save some time we'll only pick up numerics 319 (channel list from /whois nick) and 317 (idle timefrom /whois nick nick)
def cb_317(data, signal, signal_data)
outpath = Weechat.config_get_plugin('outpath')
nick = signal_data.split(' ')[3]
params = signal_data.split(' ')
time = params[4].to_i
hours = (time/3600).to_i
minutes = (time/60 - hours * 60).to_i
seconds = (time - (minutes * 60 + hours * 3600))
@bots[nick][:idle] = '%02d:%02d:%02d' % [hours, minutes, seconds]
write_file(nil, nil)
# fp = File.open(File.expand_path(File.join(outpath,'botlistdetail')),'a+') #botwhois
# fp.printf("%s idle %02d:%02d:%02d\n", nick, hours, minutes, seconds)
# fp.close
return Weechat::WEECHAT_RC_OK
end
def cb_319(data, signal, signal_data)
outpath = Weechat.config_get_plugin('outpath')
nick = signal_data.split(' ')[3]
parsed_data = signal_data.split(':')[-1]
# Weechat.print("", "Parsed data: #{parsed_data}")
@bots[nick][:channels] = parsed_data
write_file(nil, nil)
# fp = File.open(File.expand_path(File.join(outpath,'botlistdetail')),'a+')
# fp.puts(nick + ' on ' + signal_data.split(':')[-1])
# fp.close
return Weechat::WEECHAT_RC_OK
end
def parser_cb(data, remaining_calls)
sbuf = Weechat.info_get("irc_buffer", Weechat.buffer_get_string(Weechat.current_buffer(), 'localvar_server'))
# outpath = Weechat.config_get_plugin('outpath')
# botlist = File.expand_path(File.join(outpath,'botlist'))
# botdetail = File.expand_path(File.join(outpath,'botlistdetail'))
# fpd = File.open(File.expand_path(File.join(outpath,'botlistdetail')),'a+')
# File.readlines(File.expand_path(File.join(outpath,'botlist'))).each do |bn|
@bots.keys.each_with_index do |bn,index|
Weechat.print("", "Setting timer to get bot info for #{bn.strip} in #{3+index} seconds.")
Weechat.hook_timer((3+index)*1000, 0, 1, 'botparse_cb', bn.strip)
end
# fpd.close
Weechat.print(sbuf, 'Bot List dump complete')
write_file(nil, nil)
return Weechat::WEECHAT_RC_OK
end
def botparse_cb(data, remaining_calls)
sbuf = Weechat.info_get("irc_buffer", Weechat.buffer_get_string(Weechat.current_buffer(), 'localvar_server'))
# Weechat.print("", "botparse_cb Data: #{data.inspect}")
Weechat.command(sbuf, "/quote BotServ info #{data} all")
Weechat.command(sbuf, "/quote whois #{data}")
Weechat.command(sbuf, "/quote whois #{data} #{data}")
return Weechat::WEECHAT_RC_OK
end
def parsebotlist_cb(data, buffer, args)
sbuf = Weechat.info_get("irc_buffer", Weechat.buffer_get_string(Weechat.current_buffer(), 'localvar_server'))
# outpath = Weechat.config_get_plugin('outpath')
# botlist = File.expand_path(File.join(outpath,'botlist'))
# botdetail = File.expand_path(File.join(outpath,'botlistdetail'))
# Clear out all of the files
# FileUtils.touch(botlist)
# FileUtils.touch(botdetail)
# File.truncate(botlist, 0)
# File.truncate(botdetail, 0)
Weechat.command(sbuf, "/quote BotServ botlist")
Weechat.print(sbuf, 'Waiting 10 seconds for /bs botlist to finish...')
Weechat.hook_timer(10*1000, 0, 1, 'parser_cb', '') # Alternatively, as close to 'wait' as we can get
return Weechat::WEECHAT_RC_OK
end
def write_file(data, buffer)
outpath = Weechat.config_get_plugin('outpath')
File.open(File.expand_path(File.join(outpath,'parsed_botlist.md')),'w+') do |f|
f.puts "# Bots Overview:"
f.puts "==="
f.puts
@bots.group_by{|bot_nicks,bot|bot[:type]}.each do |bot_type, bots|
if bot_type == :public
f.puts "## #{bots.map{|bot_nick,bot|bot_nick}.size} Public Bots"
f.puts
elsif bot_type == :private
f.puts
f.puts "==="
f.puts
f.puts "## #{bots.map{|bot_nick,bot|bot_nick}.size} Private Bots"
f.puts
# f.puts "#{'-' * 30}= Private Bots =#{'-' * 30}"
else
f.puts "==="
f.puts
f.puts "## #{bots.map{|bot_nick,bot|bot_nick}.size} Unknown Bots"
f.puts
end
bots.each do |bot_nick, bot|
next if bot[:channels].nil?
bot[:channels] = bot[:channels].split(' ') if bot[:channels].kind_of?(String)
end
bots.sort_by{|bot_nick, bot|[bot[:channels].to_a.size, bot_nick.downcase]}.each do |bot_nick, bot|
channels = bot[:channels].to_a
number_of_channels = channels.size
if number_of_channels > 0
channels_text = "#{number_of_channels} channel#{'s' unless number_of_channels == 1}: #{channels.sort.join(', ')}"
else
channels_text = 'no channels'
end
f.puts " - #{bot_nick}!#{bot[:ident]}@#{bot[:host]}:#{bot[:realname]} [#{channels_text}]"
end
end
f.puts
end
return Weechat::WEECHAT_RC_OK
end
# Reference raw output here
# 00:38:24 Chat4All <-- | WHOIS watchdog
# 00:38:24 Chat4All --> | :eu.chat4all.org 311 o_o WatchdoG BotServ irc.chat4all.org * :Chat4All Bot:\02 /join #help \02for more information
# 00:38:24 Chat4All --> | :eu.chat4all.org 379 o_o WatchdoG :is using modes +Sq
# 00:38:24 Chat4All --> | :eu.chat4all.org 378 o_o WatchdoG :is connecting from *@irc.chat4all.org
# 00:38:24 Chat4All --> | :eu.chat4all.org 319 o_o WatchdoG :&#trivia &#lounge &#ircops &#idlerpg &#help &#chat4all
# 00:38:24 Chat4All --> | :eu.chat4all.org 312 o_o WatchdoG services.chat4all.org :Chat4All Services
# 00:38:24 Chat4All --> | :eu.chat4all.org 313 o_o WatchdoG :is a Network Service
# 00:38:24 Chat4All --> | :eu.chat4all.org 318 o_o watchdog :End of /WHOIS list.
# 00:38:26 Chat4All <-- | WHOIS watchdog watchdog
# 00:38:27 Chat4All --> | :services.chat4all.org 311 o_o WatchdoG BotServ irc.chat4all.org * :Chat4All Bot:\02 /join #help \02for more information
# 00:38:27 Chat4All --> | :services.chat4all.org 307 o_o WatchdoG :is a registered nick
# 00:38:27 Chat4All --> | :services.chat4all.org 312 o_o WatchdoG services.chat4all.org :Chat4All Services
# 00:38:27 Chat4All --> | :services.chat4all.org 317 o_o WatchdoG 20356 1334736301 :seconds idle, signon time
# 00:38:27 Chat4All --> | :services.chat4all.org 318 o_o WatchdoG :End of /WHOIS list.
# 00:47:57 Chat4All <-- | bs info watchdog
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o :Information for bot \02WatchdoG\02:
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : Mask : BotServ@irc.chat4all.org
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : Real name : Chat4All Bot:\02 /join #help \02for more information
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : Created : Dec 04 16:10:45 2003 CET
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : Options : Private
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : Used on : 14 channel(s)
# 00:47:58 Chat4All --> | :BotServ!services@chat4all.org NOTICE o_o : #beginner #casual #chat4all #class #cservice #help #HelpDesk #idleRPG #irchelp #ircops #lounge #mirc #trivia #worldchat
# 17:24:04 Chat4All <-- | bs botlist
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity :Bot list:
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : aike (aike@is.mijn.naam)
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : AkhadaQueen (info@indiancyberarmy.org)
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : Anna (Anna@Boten.Anna)
# ...
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity :Bots reserved to IRC operators:
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : _Bruce_ (Benzje@TossRadio.nl)
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : Aladdin (Aladdin@Thousand.And.One.Tales)
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : buttkicker (kick@kickchannel.rpg)
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity : Buzz (Buzz@infinity.and.beyond)
# ...
# 17:24:05 Chat4All --> | :BotServ!services@chat4all.org NOTICE Unknown_Entity :75 bots available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment