]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/salut.rb
salut plugin: don't try matching something when we know nothing
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / salut.rb
1 # vim: set sw=2 et:\r
2 # Salutations plugin: respond to salutations\r
3 # TODO allow online editing of salutations\r
4 # TODO *REMEMBER* to set @changed to true after edit\r
5 # TODO or changes won't be saved\r
6 \r
7 unless Array.respond_to?(:pick_one)\r
8   debug "Defining the pick_one method for Array"\r
9   class ::Array\r
10     def pick_one\r
11       return nil if self.empty?\r
12       self[rand(self.length)]\r
13     end\r
14   end\r
15 end\r
16 \r
17 \r
18 class SalutPlugin < Plugin\r
19   BotConfig.register BotConfigBooleanValue.new('salut.all_languages',\r
20     :default => true, \r
21     :desc => "Check for a salutation in all languages and not just in the one defined by core.language",\r
22     :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload}\r
23   )\r
24   BotConfig.register BotConfigBooleanValue.new('salut.address_only',\r
25     :default => true, \r
26     :desc => "When set to true, the bot will only reply to salutations directed at him",\r
27     :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload}\r
28   )\r
29 \r
30 \r
31   def initialize\r
32     @salutations = Hash.new\r
33     @match = nil\r
34     @main_lang_str = nil\r
35     @main_lang = nil\r
36     @all_langs = true\r
37     @changed = false\r
38     super\r
39     reload\r
40   end\r
41 \r
42   def set_language(what)\r
43     reload\r
44   end\r
45 \r
46   def create_match\r
47     @match = Hash.new\r
48     ar_dest = Array.new\r
49     ar_in = Array.new\r
50     ar_out = Array.new\r
51     ar_both = Array.new\r
52     @salutations.each { |lang, hash|\r
53       hash.each { |situation, array|\r
54         case situation.to_s\r
55         when /^generic-dest$/\r
56           ar_dest += array\r
57         when /in$/\r
58           ar_in += array\r
59         when /out$/\r
60           ar_out += array\r
61         else\r
62           ar_both += array\r
63         end\r
64       }\r
65     }\r
66     @match[:in] = Regexp.new("\\b(?:" + ar_in.uniq.map { |txt|\r
67       Regexp.escape(txt)\r
68     }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_in.empty?\r
69     @match[:out] = Regexp.new("\\b(?:" + ar_out.uniq.map { |txt|\r
70       Regexp.escape(txt)\r
71     }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_out.empty?\r
72     @match[:both] = Regexp.new("\\b(?:" + ar_both.uniq.map { |txt|\r
73       Regexp.escape(txt)\r
74     }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_both.empty?\r
75     debug "Matches: #{@match.inspect}"\r
76     @match[:dest] = Regexp.new("\\b(?:" + ar_dest.uniq.map { |txt|\r
77       Regexp.escape(txt)\r
78     }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_dest.empty?\r
79     @punct = /\s*[.,:!;?]?\s*/ # Punctuation\r
80   end\r
81 \r
82   def listen(m)\r
83     return unless @match\r
84     return unless m.kind_of?(PrivMessage)\r
85     to_me = m.address? || m.message =~ /#{Regexp.escape(@bot.nick)}/i\r
86     if @bot.config['salut.address_only']\r
87       return unless to_me\r
88     end\r
89     salut = nil\r
90     [:both, :in, :out].each { |k|\r
91       next unless @match[k]\r
92       debug "Checking salutations #{k} (#{@match[k].inspect})"\r
93       if m.message =~ @match[k]\r
94         salut = k\r
95         break\r
96       end\r
97     }\r
98     return unless salut\r
99     # If the bot wasn't addressed, we continue only the match was exact\r
100     # (apart from space and punctuation) or if @match[:dest] matches too\r
101     return unless to_me or m.message =~ @match[:dest] or m.message =~ /^#{@punct}#{@match[salut]}#{@punct}$/\r
102     h = Time.new.hour\r
103     case h\r
104     when 4...12\r
105       salut_reply(m, salut, :morning)\r
106     when 12...18\r
107       salut_reply(m, salut, :afternoon)\r
108     else\r
109       salut_reply(m, salut, :evening)\r
110     end\r
111   end\r
112 \r
113   def salut_reply(m, k, time)\r
114     debug "Replying to #{k} in the #{time}"\r
115     case k\r
116     when :both\r
117       sfx = ""\r
118     else\r
119       sfx = "-#{k}"\r
120     end\r
121     debug "Building array ..."\r
122     rep_ar = Array.new\r
123     rep_ar += @salutations[@main_lang].fetch("#{time}#{sfx}".to_sym, [])\r
124     rep_ar += @salutations[@main_lang].fetch("#{time}".to_sym, []) unless sfx.empty?\r
125     rep_ar += @salutations[@main_lang].fetch("generic#{sfx}".to_sym, [])\r
126     rep_ar += @salutations[@main_lang].fetch("generic".to_sym, []) unless sfx.empty?\r
127     debug "Choosing reply in #{rep_ar.inspect} ..."\r
128     if rep_ar.empty?\r
129       if m.public? # and (m.address? or m =~ /#{Regexp.escape(@bot.nick)}/)\r
130         choice = @bot.lang.get("hello_X") % m.sourcenick\r
131       else\r
132         choice = @bot.lang.get("hello") % m.sourcenick\r
133       end\r
134     else\r
135       choice = rep_ar.pick_one\r
136       if m.public? and (m.address? or m.message =~ /#{Regexp.escape(@bot.nick)}/)\r
137         choice += "#{[',',''].pick_one} #{m.sourcenick}"\r
138         choice += [" :)", " :D", "!", "", "", ""].pick_one\r
139       end\r
140     end\r
141     debug "Replying #{choice}"\r
142     m.plainreply choice\r
143   end\r
144 \r
145   def reload\r
146     save\r
147     @main_lang_str = @bot.config['core.language']\r
148     @main_lang = @main_lang_str.to_sym\r
149     @all_langs = @bot.config['salut.all_languages']\r
150     if @all_langs\r
151       # Get all available languages\r
152       langs = Dir.new("#{@bot.botclass}/salut").collect {|f|\r
153         f =~ /salut-([^.]+)/ ? $1 : nil\r
154       }.compact\r
155       langs.each { |lang|\r
156         @salutations[lang.to_sym] = load_lang(lang)\r
157       }\r
158     else\r
159       @salutations.clear\r
160       @salutations[@main_lang] = load_lang(@main_lang_str)\r
161     end\r
162     create_match\r
163     @changed = false\r
164   end\r
165 \r
166   def load_lang(lang)\r
167     dir = "#{@bot.botclass}/salut"\r
168     if not File.exist?(dir)\r
169       Dir.mkdir(dir)\r
170     end\r
171     file = "#{@bot.botclass}/salut/salut-#{lang}"\r
172     if File.exist?(file)\r
173       begin\r
174         salutations = Hash.new\r
175         content = YAML::load_file(file)\r
176         content.each { |key, val|\r
177           salutations[key.to_sym] = val\r
178         }\r
179         return salutations\r
180       rescue\r
181         error "failed to read salutations in #{lang}: #{$!}"\r
182       end\r
183     end\r
184     return nil\r
185   end\r
186 \r
187   def save\r
188     return if @salutations.empty?\r
189     return unless @changed\r
190     @salutations.each { |lang, val|\r
191       l = lang.to_s\r
192       save_lang(lang, val)\r
193     }\r
194     @changed = false\r
195   end\r
196 \r
197   def save_lang(lang, val)\r
198     fn = "#{@bot.botclass}/salut/salut-#{lang}"\r
199     Utils.safe_save(fn) { |file|\r
200       file.puts val.to_yaml\r
201     }\r
202   end\r
203 \r
204 end\r
205 \r
206 plugin = SalutPlugin.new\r
207 \r