+ (delicios.rb) support user-supplied tags for del.icio.us logging
[rbot] / data / rbot / plugins / topic.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Topic manipulation plugin for rbot
5 #
6 # Author:: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2006-2007 Giuseppe Bilotta
8 # License:: GPL v2
9 #
10 # Add a bunch of topic manipulation features
11
12 class TopicPlugin < Plugin
13   def initialize
14     super
15     @separator = "|" # default separator
16   end
17
18   def help(plugin, topic="")
19     case plugin
20     when "topic"
21       case topic
22       when "add"
23         return "topic add <text> => add <text> at the end the topic"
24       when "prepend"
25         return "topic prepend <text> => add <text> at the beginning of the topic"
26       when "addat"
27         return "topic addat <num> <text> => add <text> at position <num> of the topic"
28       when "del", "delete"
29         return "topic del <num> => remove section <num> from the topic"
30       when "replace"
31         return "topic replace <num> <text> => Replaces section <num> with <text>"
32       when "sep", "separator"
33         return "topic sep(arator) [<text>] => get or set the topic section separator"
34       when "learn"
35         return "topic learn => remembers the topic for later"
36       when "restore"
37         return "topic restore => resets the topic to the latest remembered one"
38       when "clear"
39         return "topic clear => clears the topic"
40       when "set"
41         return "topic set <text> => sets the topic to <text>"
42       when "undo"
43         return "topic undo => undoes the latest change to the topic"
44       else
45         return "topic add(at)|prepend|del(ete)|replace|sep(arator)|learn|restore|clear|set|undo: " + \
46                "manipulate the topic of the current channel; use topic <#channel> <command> " + \
47                "for private addressing"
48       end
49     end
50   end
51
52   def handletopic(m, param)
53     return unless m.kind_of?(PrivMessage)
54     if m.public?
55       ch = m.channel
56     else
57       ch = m.server.get_channel(param[:channel])
58       unless ch
59         m.reply("I am not in channel #{param[:channel]}")
60         return
61       end
62     end
63     cmd = param[:command]
64     txt = param[:text].to_s
65
66     case cmd
67     when /^a(dd|ppend)$/
68       topicappend(m, ch, txt)
69     when 'prepend'
70       topicprepend(m, ch, txt)
71     when 'addat'
72       if txt =~ /\s*(-?\d+)\s+(.*)\s*/
73         num = $1.to_i - 1
74         num += 1 if num < 0
75         txt = $2
76         topicaddat(m, ch, num, txt)
77       end
78     when /^del(ete)?$/
79       if txt =~ /\s*(-?\d+)\s*/
80         num=$1.to_i - 1
81         num += 1 if num < 0
82         topicdel(m, ch, num)
83       end
84     when 'set'
85       topicset(m, ch, txt)
86     when 'clear'
87       topicset(m, ch, '')
88     when /^sep(arator)?$/
89       topicsep(m, ch, txt)
90     when 'learn'
91       learntopic(m, ch)
92     when 'replace'
93       if txt =~ /\s*(-?\d+)\s+(.*)\s*/
94         num = $1.to_i - 1
95         num += 1 if num < 0
96         txt = $2
97         replacetopic(m, ch, num, txt)
98       end
99     when 'restore'
100       restoretopic(m, ch)
101     when 'undo'
102       undotopic(m, ch)
103     else
104       m.reply 'unknown command'
105     end
106   end
107
108   def topicsep(m, ch, txt)
109     return if !@bot.auth.allow?("topic::edit::separator", m.source, m.replyto)
110     if txt
111       sep = txt.strip
112       if sep != ""
113         setsep(ch, sep)
114       end
115     end
116     m.reply "Topic separator set to #{getsep(ch)}"
117   end
118
119   def setsep(ch, sep)
120     raise unless ch.class <= Irc::Channel
121     # TODO multiserver
122     k = ch.downcase
123
124     if @registry.has_key?(k)
125       data = @registry[k]
126     else
127       data = Hash.new
128     end
129
130     oldsep = getsep(ch)
131     topic = ch.topic.text
132     topicarray = topic.split(/\s+#{Regexp.escape(oldsep)}\s*/)
133
134     if sep != oldsep and topicarray.length > 0
135       newtopic = topicarray.join(" #{sep} ")
136       @bot.topic ch, newtopic if newtopic != topic
137     end
138
139     data[:separator] = sep
140     @registry[k] = data
141   end
142
143   def getsep(ch)
144     raise unless ch.class <= Irc::Channel
145     # TODO multiserver
146     k = ch.downcase
147
148     if @registry.has_key?(k)
149       if @registry[k].has_key?(:separator)
150         return @registry[k][:separator]
151       end
152     end
153     return @separator
154   end
155
156   def topicaddat(m, ch, num, txt)
157     return if !@bot.auth.allow?("topic::edit::add", m.source, m.replyto)
158     sep = getsep(ch)
159     topic = ch.topic.text
160     topicarray = topic.split(/\s+#{Regexp.escape(sep)}\s*/)
161     topicarray.insert(num, txt)
162     newtopic = topicarray.join(" #{sep} ")
163     changetopic(m, ch, newtopic)
164   end
165
166   def topicappend(m, ch, txt)
167     topicaddat(m, ch, -1, txt)
168   end
169
170   def topicprepend(m, ch, txt)
171     topicaddat(m, ch, 0, txt)
172   end
173
174   def topicdel(m, ch, num)
175     return if !@bot.auth.allow?("topic::edit::del", m.source, m.replyto)
176     sep = getsep(ch)
177     topic = ch.topic.text
178     topicarray = topic.split(/\s+#{Regexp.escape(sep)}\s*/)
179     topicarray.delete_at(num)
180     newtopic = topicarray.join(" #{sep} ")
181     changetopic(m, ch, newtopic)
182   end
183
184   def learntopic(m, ch)
185     return if !@bot.auth.allow?("topic::store::store", m.source, m.replyto)
186     topic = ch.topic.text
187     k = ch.downcase
188     if @registry.has_key?(k)
189       data = @registry[k]
190     else
191       data = Hash.new
192     end
193     data[:topic] = topic
194     @registry[k] = data
195     m.okay
196   end
197
198   def replacetopic(m, ch, num, txt)
199     return if !@bot.auth.allow?("topic::edit::replace", m.source, m.replyto)
200     sep = getsep(ch)
201     topic = ch.topic.text
202     topicarray = topic.split(/\s+#{Regexp.escape(sep)}\s*/)
203     topicarray[num] = txt
204     newtopic = topicarray.join(" #{sep} ")
205     changetopic(m, ch, newtopic)
206   end
207
208   def restoretopic(m, ch)
209     return if !@bot.auth.allow?("topic::store::restore", m.source, m.replyto)
210     k = ch.downcase
211     if @registry.has_key?(k) && @registry[k].has_key?(:topic)
212       topic = @registry[k][:topic]
213       topicset(m, ch, topic)
214     else
215       m.reply "I don't remember any topic for #{ch}"
216     end
217   end
218
219   def topicset(m, ch, text)
220     return if !@bot.auth.allow?("topic::edit::replace", m.source, m.replyto)
221     changetopic(m, ch, text)
222   end
223
224   # This method changes the topic on channel +ch+ to +text+, storing
225   # the previous topic for undo
226   def changetopic(m, ch, text)
227     k = ch.downcase
228     if @registry.has_key?(k)
229       data = @registry[k]
230     else
231       data = Hash.new
232     end
233
234     data[:oldtopic] = ch.topic.text
235     @registry[k] = data
236
237     @bot.topic ch, text
238   end
239
240   def undotopic(m, ch)
241     k = ch.downcase
242     if @registry.has_key?(k)
243       data = @registry[k]
244       if data.has_key?(:oldtopic)
245         changetopic(m, ch, data[:oldtopic].dup)
246         return
247       end
248     end
249
250     m.reply "No recent changes were recorded for #{ch}"
251   end
252
253 end
254 plugin = TopicPlugin.new
255
256 plugin.map 'topic :command [*text]', :action => 'handletopic', :public => true, :private => false
257 plugin.map 'topic :channel :command [*text]', :action => 'handletopic', :public => false, :private => true
258
259 plugin.default_auth('*', false)
260
261