-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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