]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/factoids.rb
59f1a06bbda3a5b253a39b258c0b6f145938d85c
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / factoids.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Factoids pluing
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2007 Giuseppe Bilotta
8 # License:: GPLv2
9 #
10 # Store (and retrieve) unstructured one-sentence factoids
11
12 class FactoidsPlugin < Plugin
13
14   class Factoid
15     def initialize(hash)
16       @hash = hash.reject { |k, val| val.nil? or val.empty? rescue false }
17       raise ArgumentError, "no fact!" unless @hash[:fact]
18       if String === @hash[:when]
19         @hash[:when] = Time.parse @hash[:when]
20       end
21     end
22
23     def to_s
24       @hash[:fact]
25     end
26
27     def [](*args)
28       @hash[*args]
29     end
30
31     def to_hsh
32       return @hash
33     end
34     alias :to_hash :to_hsh
35   end
36
37   class FactoidList < ArrayOf
38     def initialize(ar=[])
39       super(Factoid, ar)
40     end
41
42     def index(f)
43       fact = f.to_s
44       return if fact.empty?
45       self.map { |f| f[:fact] }.index(fact)
46     end
47
48     def delete(f)
49       idx = index(f)
50       return unless idx
51       self.delete_at(idx)
52     end
53
54     def grep(x)
55       self.find_all { |f|
56         x === f[:fact]
57       }
58     end
59   end
60
61   def initialize
62     super
63
64     # TODO config
65     @dir = File.join(@bot.botclass,"factoids")
66     @filename = "factoids.rbot"
67     @factoids = FactoidList.new
68     read_factfile
69     @changed = false
70   end
71
72   def read_factfile(name=@filename,dir=@dir)
73     fname = File.join(dir,name)
74     if File.exist?(fname)
75       factoids = File.readlines(fname)
76       return if factoids.empty?
77       firstline = factoids.shift
78       pattern = firstline.chomp.split(" | ")
79       if pattern.length == 1 and pattern.first != "fact"
80         factoids.unshift(firstline)
81         factoids.each { |f|
82           @factoids << Factoid.new( :fact => f.chomp )
83         }
84       else
85         pattern.map! { |p| p.intern }
86         raise ArgumentError, "fact must be the last field" unless pattern.last == :fact
87         factoids.each { |f|
88           ar = f.chomp.split(" | ", pattern.length)
89           @factoids << Factoid.new(Hash[*([pattern, ar].transpose.flatten)])
90         }
91       end
92     end
93   end
94
95   def save
96     return unless @changed
97     Dir.mkdir(@dir) unless FileTest.directory?(@dir)
98     fname = File.join(@dir,@filename)
99     ar = ["when | who | where | fact"]
100     @factoids.each { |f|
101       ar << "%s | %s | %s | %s" % [ f[:when], f[:who], f[:where], f[:fact]]
102     }
103     Utils.safe_save(fname) do |file|
104       file.puts ar
105     end
106     @changed = false
107   end
108
109   def help(plugin, topic="")
110     _("factoids plugin: learn that <factoid>, forget that <factoids>, facts about <words>")
111   end
112
113   def learn(m, params)
114     factoid = Factoid.new(
115       :fact => params[:stuff].to_s,
116       :when => Time.now,
117       :who => m.source.fullform,
118       :where => m.channel.to_s
119     )
120     if @factoids.index(factoid)
121       m.reply _("I already know that %{factoid}" % { :factoid => factoid })
122     else
123       @factoids << factoid
124       @changed = true
125       m.okay
126     end
127   end
128
129   def forget(m, params)
130     factoid = params[:stuff].to_s
131     if @factoids.delete(factoid)
132       @changed = true
133       m.okay
134     else
135       m.reply _("I didn't know that %{factoid}" % { :factoid => factoid })
136     end
137   end
138
139   def facts(m, params)
140     if params[:words].empty?
141       m.reply _("I know %{count} facts" % { :count => @factoids.length })
142     else
143       rx = Regexp.new(params[:words].to_s, true)
144       known = @factoids.grep(rx)
145       if known.empty?
146         m.reply _("I know nothing about %{words}" % params)
147       else
148         m.reply known.join(" | "), :split_at => /\s+\|\s+/
149       end
150     end
151   end
152
153   def fact(m, params)
154     fact = nil
155     idx = 0
156     total = @factoids.length
157     if params[:index]
158       idx = params[:index].scan(/\d+/).first.to_i
159       if idx <= 0 or idx > total
160         m.reply _("please select a fact number between 1 and %{total}" % { :total => total })
161         return
162       end
163       fact = @factoids[idx-1]
164     else
165       known = nil
166       if params[:words].empty?
167         if @factoids.empty?
168           m.reply _("I know nothing")
169           return
170         end
171         known = @factoids
172       else
173         rx = Regexp.new(params[:words].to_s, true)
174         known = @factoids.grep(rx)
175         if known.empty?
176           m.reply _("I know nothing about %{words}" % params)
177           return
178         end
179       end
180       fact = known.pick_one
181       idx = @factoids.index(fact)+1
182     end
183     meta = nil
184     metadata = []
185     if fact[:who]
186       metadata << _("from %{who}" % fact.to_hash)
187     end
188     if fact[:when]
189       metadata << _("on %{when}" % fact.to_hash)
190     end
191     if fact[:where]
192       metadata << _("in %{where}" % fact.to_hash)
193     end
194     unless metadata.empty?
195       meta = _(" [learnt %{data}]" % {:data => metadata.join(" ")})
196     end
197     m.reply _("fact #%{idx} of %{total}: %{fact}%{meta}" % {
198       :idx => idx,
199       :total => total,
200       :fact => fact,
201       :meta => meta
202     })
203   end
204
205 end
206
207 plugin = FactoidsPlugin.new
208
209 plugin.default_auth('edit', false)
210
211 plugin.map 'learn that *stuff'
212 plugin.map 'forget that *stuff', :auth_path => 'edit'
213 plugin.map 'facts [about *words]'
214 plugin.map 'fact [about *words]'
215 plugin.map 'fact :index', :requirements => { :index => /#?\d+/ }