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