]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/salut.rb
e4295a06655c84323fe9d6fb52a886701a5cfd75
[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.new.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 = Hash.new\r
34     @match_langs = Array.new\r
35     @main_lang_str = nil\r
36     @main_lang = nil\r
37     @all_langs = true\r
38     @changed = false\r
39     super\r
40     reload\r
41   end\r
42 \r
43   def set_language(what)\r
44     reload\r
45   end\r
46 \r
47   def create_match\r
48     @match.clear\r
49     ar_dest = Array.new\r
50     ar_in = Array.new\r
51     ar_out = Array.new\r
52     ar_both = Array.new\r
53     @salutations.each { |lang, hash|\r
54       ar_dest.clear\r
55       ar_in.clear\r
56       ar_out.clear\r
57       ar_both.clear\r
58       hash.each { |situation, array|\r
59         case situation.to_s\r
60         when /^generic-dest$/\r
61           ar_dest += array\r
62         when /in$/\r
63           ar_in += array\r
64         when /out$/\r
65           ar_out += array\r
66         else\r
67           ar_both += array\r
68         end\r
69       }\r
70       @match[lang] = Hash.new\r
71       @match[lang][:in] = Regexp.new("\\b(?:" + ar_in.uniq.map { |txt|\r
72         Regexp.escape(txt)\r
73       }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_in.empty?\r
74       @match[lang][:out] = Regexp.new("\\b(?:" + ar_out.uniq.map { |txt|\r
75         Regexp.escape(txt)\r
76       }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_out.empty?\r
77       @match[lang][:both] = Regexp.new("\\b(?:" + ar_both.uniq.map { |txt|\r
78         Regexp.escape(txt)\r
79       }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_both.empty?\r
80       @match[lang][:dest] = Regexp.new("\\b(?:" + ar_dest.uniq.map { |txt|\r
81         Regexp.escape(txt)\r
82       }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_dest.empty?\r
83     }\r
84     @punct = /\s*[.,:!;?]?\s*/ # Punctuation\r
85 \r
86     # Languages to match for, in order\r
87     @match_langs.clear\r
88     @match_langs << @main_lang if @match.key?(@main_lang)\r
89     @match_langs << :english if @match.key?(:english)\r
90     @match.each_key { |key|\r
91       @match_langs << key\r
92     }\r
93     @match_langs.uniq!\r
94   end\r
95 \r
96   def listen(m)\r
97     return if @match.empty?\r
98     return unless m.kind_of?(PrivMessage)\r
99     return if m.address? and m.plugin == 'config'\r
100     to_me = m.address? || m.message =~ /#{Regexp.escape(@bot.nick)}/i\r
101     if @bot.config['salut.address_only']\r
102       return unless to_me\r
103     end\r
104     salut = nil\r
105     @match_langs.each { |lang|\r
106       [:both, :in, :out].each { |k|\r
107         next unless @match[lang][k]\r
108         if m.message =~ @match[lang][k]\r
109           salut = [@match[lang][k], lang, k]\r
110           break\r
111         end\r
112       }\r
113       break if salut\r
114     }\r
115     return unless salut\r
116     # If the bot wasn't addressed, we continue only if the match was exact\r
117     # (apart from space and punctuation) or if @match[:dest] matches too\r
118     return unless to_me or m.message =~ /^#{@punct}#{salut.first}#{@punct}$/ or m.message =~ @match[salut[1]][:dest] \r
119     h = Time.new.hour\r
120     case h\r
121     when 4...12\r
122       salut_reply(m, salut, :morning)\r
123     when 12...18\r
124       salut_reply(m, salut, :afternoon)\r
125     else\r
126       salut_reply(m, salut, :evening)\r
127     end\r
128   end\r
129 \r
130   def salut_reply(m, salut, time)\r
131     lang = salut[1]\r
132     k = salut[2]\r
133     debug "Replying to #{salut.first} (#{lang} #{k}) in the #{time}"\r
134     # salut_ar = @salutations[@main_lang].update @salutations[:english].update @salutations[lang]\r
135     salut_ar = @salutations[lang]\r
136     case k\r
137     when :both\r
138       sfx = ""\r
139     else\r
140       sfx = "-#{k}"\r
141     end\r
142     debug "Building array ..."\r
143     rep_ar = Array.new\r
144     rep_ar += salut_ar.fetch("#{time}#{sfx}".to_sym, [])\r
145     rep_ar += salut_ar.fetch("#{time}".to_sym, []) unless sfx.empty?\r
146     rep_ar += salut_ar.fetch("generic#{sfx}".to_sym, [])\r
147     rep_ar += salut_ar.fetch("generic".to_sym, []) unless sfx.empty?\r
148     debug "Choosing reply in #{rep_ar.inspect} ..."\r
149     if rep_ar.empty?\r
150       if m.public? # and (m.address? or m =~ /#{Regexp.escape(@bot.nick)}/)\r
151         choice = @bot.lang.get("hello_X") % m.sourcenick\r
152       else\r
153         choice = @bot.lang.get("hello") % m.sourcenick\r
154       end\r
155     else\r
156       choice = rep_ar.pick_one\r
157       if m.public? and (m.address? or m.message =~ /#{Regexp.escape(@bot.nick)}/)\r
158         choice += "#{[',',''].pick_one} #{m.sourcenick}"\r
159         choice += [" :)", " :D", "!", "", "", ""].pick_one\r
160       end\r
161     end\r
162     debug "Replying #{choice}"\r
163     m.plainreply choice\r
164   end\r
165 \r
166   def reload\r
167     save\r
168     @main_lang_str = @bot.config['core.language']\r
169     @main_lang = @main_lang_str.to_sym\r
170     @all_langs = @bot.config['salut.all_languages']\r
171     if @all_langs\r
172       # Get all available languages\r
173       langs = Dir.new("#{@bot.botclass}/salut").collect {|f|\r
174         f =~ /salut-([^.]+)/ ? $1 : nil\r
175       }.compact\r
176       langs.each { |lang|\r
177         @salutations[lang.to_sym] = load_lang(lang)\r
178       }\r
179     else\r
180       @salutations.clear\r
181       @salutations[@main_lang] = load_lang(@main_lang_str)\r
182     end\r
183     create_match\r
184     @changed = false\r
185   end\r
186 \r
187   def load_lang(lang)\r
188     dir = "#{@bot.botclass}/salut"\r
189     if not File.exist?(dir)\r
190       Dir.mkdir(dir)\r
191     end\r
192     file = "#{@bot.botclass}/salut/salut-#{lang}"\r
193     if File.exist?(file)\r
194       begin\r
195         salutations = Hash.new\r
196         content = YAML::load_file(file)\r
197         content.each { |key, val|\r
198           salutations[key.to_sym] = val\r
199         }\r
200         return salutations\r
201       rescue\r
202         error "failed to read salutations in #{lang}: #{$!}"\r
203       end\r
204     end\r
205     return nil\r
206   end\r
207 \r
208   def save\r
209     return if @salutations.empty?\r
210     return unless @changed\r
211     @salutations.each { |lang, val|\r
212       l = lang.to_s\r
213       save_lang(lang, val)\r
214     }\r
215     @changed = false\r
216   end\r
217 \r
218   def save_lang(lang, val)\r
219     fn = "#{@bot.botclass}/salut/salut-#{lang}"\r
220     Utils.safe_save(fn) { |file|\r
221       file.puts val.to_yaml\r
222     }\r
223   end\r
224 \r
225 end\r
226 \r
227 plugin = SalutPlugin.new\r
228 \r