Files
2017-02-25 01:41:30 +11:00

411 lines
9.5 KiB
Ruby

#
# port.rb
#
# Plugin to rbot (http://ruby-rbot.org/), an irc bot, to provide
# services related to MacPorts for the #macports channel on freenode.net.
#
# By James D. Berry
#
# $Id$
#
require 'fileutils.rb'
require 'stringio'
module Shuffle
def shuffle!
size.downto(2) do |i|
r = rand(i)
tmp = self[i-1]
self[i-1] = self[r]
self[r] = tmp
end
self
end
def shuffle
Array.new(self).extend(Shuffle).shuffle!
end
end
class PortPlugin < Plugin
def help(plugin, topic="")
case topic
when "info"
return "info <portname> => show info for port <portname>"
when "maintainer"
return "maintainer <portname> => show maintainer of port <portname>"
when "version"
return "version <portname> => show version of port <portname>"
when "search"
return "search <query> => show ports matching <query>"
when "herald"
return "herald enable|disable => enable or disable heralding by port"
when "remember"
return "remember <nick> email <address> => remember what port maintainer email <nick> belongs to; " +
"remember <nick> timezone <timezone> => remember the local timezone for <nick>; " +
"remember <nick> location <location> => remember the physical location for <nick>; " +
"remember <nick> name <name> => remember the real name for <nick>"
when "forget"
return "forget <nick> => forget all information for <nick>; " +
"forget email <nick> => forget maintainer email for <nick>; " +
"forget timezone <nick> => forget local timezone for <nick>; " +
"forget location <nick> => forget physical location for <nick>; " +
"forget name <nick> => forget real name for <nick>"
when "whois"
return "whois <nick> => give a summary of information for <nick>"
when "whereis"
return "whereis <nick> => tell what is known about timezone and location of <nick>"
else
return "port (MacPorts) commands: info, maintainer, version, herald, remember, forget, whois, whereis"
end
end
def runCmd(cmd, args, input=nil, output=nil, env={})
# Open pipes for stdin, and stdout
stdin, inwrite = IO.pipe
outread, stdout = IO.pipe
# Fork the child
pid = fork {
# In child
# Change the environment
ENV.update(env)
# Redirect IO to the pipes
inwrite.close
$stdin.reopen(stdin)
stdin.close
outread.close
$stdout.reopen(stdout)
$stderr.reopen(stdout)
stdout.close
# Execute the command
exec(cmd, *args)
# shouldn't return
}
# Close unneeded pipe-ends in this process
[stdin, stdout].each { |io| io.close }
# In order to avoid deadlock, invoke a secondary process to write the input
open("|-", "w+") { |p|
if (p == nil)
# In child
FileUtils.copy_stream(input, inwrite) if !input.nil?
inwrite.close
end
}
# Close remaining write pipe into command
inwrite.close
# Read the stdout from the command, writing it to output
FileUtils.copy_stream(outread, output) if !output.nil?
# Close remaining pipe ends
[outread].each { |io| io.close }
# Collect the return code
pid, rc = Process.waitpid2(pid)
rc >>= 8
return rc
end
def callPort(*args)
Utils.safe_exec("/opt/local/bin/port", *args)
end
def doPort(m, *args)
Thread.new do
text = callPort(*args)
m.reply text
end
end
def info(m, params)
if !isPseudo(m, params[:portname])
doPort(m, "info", params[:portname])
end
end
def portmaintainer(m, params)
if !isPseudo(m, params[:portname])
doPort(m, "info", "--maintainer", params[:portname])
end
end
def portversion(m, params)
if !isPseudo(m, params[:portname])
doPort(m, "info", "--version", "--revision", "--line", "--pretty", params[:portname])
end
end
def isPseudo(m, portname)
case portname
when "all", "current", "active", "inactive", "installed", "uninstalled", "outdated", "obsolete", "requested", "unrequested", "leaves"
ret = true
when /:/, /\*/, /\?/, /\{/, /\}/, /\[/, /\]/
ret = true
else
ret = false
end
if ret
m.reply "no globs or pseudo ports, please"
end
return ret
end
def portsearch(m, params)
doPort(m, "search", params[:query])
end
def herald_enable(m, params)
@registry['herald_enable'] = true
m.okay
end
def herald_disable(m, params)
@registry['herald_enable'] = false
m.okay
end
def rememberEmail(m, params)
nick = params[:nick]
email = params[:email]
@registry["email_#{nick}"] = email
m.reply "okay, #{nick} is #{email}"
end
def rememberTimeZone(m, params)
nick = params[:nick]
timezone = params[:timezone]
@registry["timezone_#{nick}"] = timezone
m.reply "okay, #{nick} is in timezone #{timezone}"
end
def rememberLocation(m, params)
nick = params[:nick]
location = params[:location].join(' ')
@registry["location_#{nick}"] = location
m.reply "okay, #{nick} is in #{location}"
end
def rememberName(m, params)
nick = params[:nick]
name = params[:name].join(' ')
@registry["name_#{nick}"] = name
m.reply "okay, #{nick} is #{name}"
end
def forget(m, params)
nick = params[:nick]
@registry.delete("email_#{nick}")
@registry.delete("timezone_#{nick}")
@registry.delete("location_#{nick}")
@registry.delete("name_#{nick}")
m.okay
end
def forgetEmail(m, params)
nick = params[:nick]
@registry.delete("email_#{nick}")
m.okay
end
def forgetTimeZone(m, params)
nick = params[:nick]
@registry.delete("timezone_#{nick}")
m.okay
end
def forgetLocation(m, params)
nick = params[:nick]
@registry.delete("location_#{nick}")
m.okay
end
def forgetName(m, params)
nick = params[:nick]
@registry.delete("name_#{nick}")
m.okay
end
def whois(m, params)
nick = params[:nick]
email = @registry["email_#{nick}"]
if email
heraldUser m.replyto, nick
else
m.reply "I don't know #{nick}"
end
end
def localTimeInTimeZone(timeZone)
localTime = nil
StringIO.open("", "r+") { |o|
runCmd("/bin/date", "+%a %H:%M %Z", nil, o, { "TZ" => timeZone } )
o.close_write
localTime = o.string
}
return localTime
end
def whereisNick(nick)
location = @registry["location_#{nick}"]
timeZone = @registry["timezone_#{nick}"]
localTime = nil
if timeZone
localTime = localTimeInTimeZone(timeZone)
end
if location && localTime
whereis = "is in #{location}; local time is #{localTime}"
elsif location
whereis = "is in #{location}"
elsif localTime
whereis = "is at local time #{localTime}"
else
whereis = nil
end
end
def whereis(m, params)
nick = params[:nick]
where = whereisNick(nick)
if where
m.reply "#{nick} #{where}"
else
m.reply "I don't know where #{nick} is"
end
end
def textEnumeration(a)
sz = a.size
case sz
when 0
return ""
when 1
return a[0]
when 2
return a.join(' and ')
else
return a.slice(0, sz-1).join(', ') + ' and ' + a[sz-1]
end
end
def heraldUser(where, nick)
Thread.new do
email = @registry["email_#{nick}"]
if email
name = @registry["name_#{nick}"]
text = callPort("echo", "maintainer:(?:^|[\s{])#{email}(?:[\s}]|$)")
ports = text.split(/\s+/)
portCount = ports.size
showMax = 4
somePorts = ports.extend(Shuffle).shuffle!.slice(0, showMax)
msg = "#{nick} is"
if name
msg += " #{name}"
end
if nick != email
if name
msg += " (#{email})"
else
msg += " #{email}"
end
end
if portCount > 0
if name || nick != email
msg += " and"
end
msg += " maintainer of " + textEnumeration(somePorts)
if portCount > showMax
msg += " (of #{portCount} total)"
end
end
whereis = whereisNick(nick)
if whereis
msg = msg + " and #{whereis}"
end
@bot.say where, msg
end
end
end
def maybeHerald(where, nick)
now = Time.new
minSecondsBetween = 60*10
doHerald = true
email = @registry["email_#{nick}"]
if (email)
lastHerald = @registry["lastherald_#{email}"]
if (lastHerald)
secondsAgo = now - lastHerald
doHerald = secondsAgo > minSecondsBetween
end
@registry["lastherald_#{email}"] = now
end
heraldUser where, nick if doHerald
end
def join(m)
maybeHerald m.target, m.sourcenick
end
def part(m)
nick = m.sourcenick
email = @registry["email_#{nick}"]
@registry["lastherald_#{email}"] = Time.new if email
end
def nick(m)
newnick = m.message
@bot.channels.each_value { |c|
if(c.users.has_key?(newnick))
maybeHerald c.name, newnick
end
}
end
end
plugin = PortPlugin.new
plugin.map 'port info :portname', :action => 'info'
plugin.map 'port maintainer :portname', :action => 'portmaintainer'
plugin.map 'port version :portname', :action => 'portversion'
#plugin.map 'port search :query', :action => 'portsearch'
plugin.map 'port herald enable', :action => 'herald_enable'
plugin.map 'port herald disable', :action => 'herald_disable'
plugin.map 'port remember :nick timezone :timezone', :action => 'rememberTimeZone'
plugin.map 'port remember :nick email :email', :action => 'rememberEmail'
plugin.map 'port remember :nick location *location', :action => 'rememberLocation'
plugin.map 'port remember :nick name *name', :action => 'rememberName'
plugin.map 'port forget :nick', :action => 'forget'
plugin.map 'port forget :nick email', :action => 'forgetEmail'
plugin.map 'port forget :nick timezone', :action => 'forgetTimeZone'
plugin.map 'port forget :nick location', :action => 'forgetLocation'
plugin.map 'port forget :nick name', :action => 'forgetName'
plugin.map 'port whois :nick', :action => 'whois'
plugin.map 'port whereis :nick', :action => 'whereis'