4 # :title: Alias plugin for rbot
6 # Author:: Yaohan Chen <yaohan.chen@gmail.com>
7 # Copyright:: (C) 2007 Yaohan Chen
10 # This plugin allows defining aliases for rbot commands. Aliases are like normal rbot
11 # commands and can take parameters. When called, they will be substituted into an
12 # exisitng rbot command and that is run.
15 # < alias googlerbot *terms => google site:ruby-rbot.org <terms>
17 # < googlerbot plugins
18 # > Results for site:ruby-rbot.org plugins: ....
21 # By default, only the owner can define and remove aliases, while everyone else can
22 # use and view them. When a command is executed with an alias, it's mapped normally with
23 # the alias user appearing to attempt to execute the command. Therefore it should be not
24 # possible to use aliases to circumvent permission sets. Care should be taken when
25 # defining aliases, due to these concerns:
26 # * Defined aliases can potentially override other plugins' maps, if this plugin is
28 # * Aliases can cause infinite recursion of aliases and/or commands. The plugin attempts
29 # to detect and stop this, but a few recursive calls can still cause spamming
34 class AliasPlugin < Plugin
35 # an exception raised when loading or getting input of invalid alias definitions
36 class AliasDefinitionError < ArgumentError
41 @aliases = @registry[:aliases]
43 # attempt to load aliases from data file yaml
44 filename = File.join(datafile, 'aliases.yaml')
45 if File.exists? filename
48 YAML.load_file(filename).each_pair do |a, c|
52 warning _("Data file is not found or corrupt, reinitializing data")
62 @registry[:aliases] = @aliases
66 def cmd_add(m, params)
68 add_alias(params[:text].to_s, params[:command].to_s)
70 rescue AliasDefinitionError
71 m.reply _('The definition you provided is invalid: %{reason}') % {:reason => $!}
75 def cmd_remove(m, params)
76 text = params[:text].to_s
77 if @aliases.has_key?(text)
79 # TODO when rbot supports it, remove the mapping corresponding to the alias
82 m.reply _('No such alias is defined')
86 def cmd_list(m, params)
88 m.reply _('No aliases defined')
90 m.reply @aliases.map {|a, c| "#{a} => #{c}"}.join(' | ')
94 def cmd_whatis(m, params)
95 text = params[:text].to_s
96 if @aliases.has_key?(text)
97 m.reply _('Alias of %{command}') % {:command => @aliases[text]}
99 m.reply _('No such alias is defined')
103 def add_alias(text, command)
104 # each alias is implemented by adding a message map, whose handler creates a message
105 # containing the aliased command
107 command.scan(/<(\w+)>/).flatten.to_set ==
108 text.split.grep(/\A[:*](\w+)\Z/) {$1}.to_set or
109 raise AliasDefinitionError.new(_('The arguments in alias must match the substitutions in command, and vice versa'))
112 map text, :action => :"alias_handle<#{text}>", :auth_path => 'run'
114 raise AliasDefinitionError.new(_('Error mapping %{text} as command: %{error}') %
115 {:text => text, :error => $!})
117 @aliases[text] = command
120 def respond_to?(name, include_private=false)
121 name.to_s =~ /\Aalias_handle<.+>\Z/ || super
124 def method_missing(name, *args, &block)
125 if name.to_s =~ /\Aalias_handle<(.+)>\Z/
129 command = @aliases[text]
132 # create a fake message containing the intended command
133 @bot.plugins.privmsg fake_message(command.gsub(/<(\w+)>/) {|arg| params[:"#{$1}"].to_s}, :from => m, :delegate => false)
134 rescue RecurseTooDeep
135 m.reply _('The alias seems to have caused infinite recursion. Please examine your alias definitions')
139 m.reply(_("Error handling the alias, The alias %{text} is not defined or has beeen removed. I will stop responding to it after rescan,") %
143 super(name, *args, &block)
147 def help(plugin, topic='')
150 if plugin == 'alias' # FIXME find out plugin name programmatically
151 _('Create and use aliases for commands. Topics: create, commands')
153 # show definition of all aliases whose first word is the parameter of the help
155 @aliases.keys.select {|a| a[/\A(\w+)/, 1] == plugin}.map do |a|
156 "#{a} => #{@aliases[a]}"
160 _('"alias <text> => <command>" => add text as an alias of command. Text can contain placeholders marked with : or * for :words and *multiword arguments. The command can contain placeholders enclosed with < > which will be substituded with argument values. For example: alias googlerbot *terms => google site:ruby-rbot.org <terms>')
162 _('alias list => list defined aliases | alias whatis <alias> => show definition of the alias | alias remove <alias> => remove defined alias | see the "create" topic about adding aliases')
167 plugin = AliasPlugin.new
168 plugin.default_auth('edit', false)
169 plugin.default_auth('run', true)
170 plugin.default_auth('list', true)
172 plugin.map 'alias list',
173 :action => :cmd_list,
175 plugin.map 'alias whatis *text',
176 :action => :cmd_whatis,
178 plugin.map 'alias remove *text',
179 :action => :cmd_remove,
181 plugin.map 'alias rm *text',
182 :action => :cmd_remove,
184 plugin.map 'alias *text => *command',