butler 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. data/CHANGELOG +4 -0
  2. data/GPL.txt +340 -0
  3. data/LICENSE.txt +52 -0
  4. data/README +37 -0
  5. data/Rakefile +334 -0
  6. data/bin/botcontrol +230 -0
  7. data/data/butler/config_template.yaml +4 -0
  8. data/data/butler/dialogs/backup.rb +19 -0
  9. data/data/butler/dialogs/botcontrol.rb +4 -0
  10. data/data/butler/dialogs/config.rb +1 -0
  11. data/data/butler/dialogs/create.rb +53 -0
  12. data/data/butler/dialogs/delete.rb +3 -0
  13. data/data/butler/dialogs/en/backup.yaml +6 -0
  14. data/data/butler/dialogs/en/botcontrol.yaml +5 -0
  15. data/data/butler/dialogs/en/create.yaml +11 -0
  16. data/data/butler/dialogs/en/delete.yaml +2 -0
  17. data/data/butler/dialogs/en/help.yaml +17 -0
  18. data/data/butler/dialogs/en/info.yaml +13 -0
  19. data/data/butler/dialogs/en/list.yaml +4 -0
  20. data/data/butler/dialogs/en/notyetimplemented.yaml +2 -0
  21. data/data/butler/dialogs/en/rename.yaml +3 -0
  22. data/data/butler/dialogs/en/start.yaml +3 -0
  23. data/data/butler/dialogs/en/sync_plugins.yaml +3 -0
  24. data/data/butler/dialogs/en/uninstall.yaml +5 -0
  25. data/data/butler/dialogs/en/unknown_command.yaml +2 -0
  26. data/data/butler/dialogs/help.rb +11 -0
  27. data/data/butler/dialogs/info.rb +27 -0
  28. data/data/butler/dialogs/interactive.rb +1 -0
  29. data/data/butler/dialogs/list.rb +10 -0
  30. data/data/butler/dialogs/notyetimplemented.rb +1 -0
  31. data/data/butler/dialogs/rename.rb +4 -0
  32. data/data/butler/dialogs/selectbot.rb +2 -0
  33. data/data/butler/dialogs/start.rb +5 -0
  34. data/data/butler/dialogs/sync_plugins.rb +30 -0
  35. data/data/butler/dialogs/uninstall.rb +17 -0
  36. data/data/butler/dialogs/unknown_command.rb +1 -0
  37. data/data/butler/plugins/core/logout.rb +41 -0
  38. data/data/butler/plugins/core/plugins.rb +134 -0
  39. data/data/butler/plugins/core/privilege.rb +103 -0
  40. data/data/butler/plugins/core/user.rb +166 -0
  41. data/data/butler/plugins/dev/eval.rb +64 -0
  42. data/data/butler/plugins/dev/nometa.rb +14 -0
  43. data/data/butler/plugins/dev/onhandlers.rb +93 -0
  44. data/data/butler/plugins/dev/raw.rb +36 -0
  45. data/data/butler/plugins/dev/rawlog.rb +77 -0
  46. data/data/butler/plugins/games/eightball.rb +54 -0
  47. data/data/butler/plugins/games/mastermind.rb +174 -0
  48. data/data/butler/plugins/irc/action.rb +36 -0
  49. data/data/butler/plugins/irc/join.rb +38 -0
  50. data/data/butler/plugins/irc/notice.rb +36 -0
  51. data/data/butler/plugins/irc/part.rb +38 -0
  52. data/data/butler/plugins/irc/privmsg.rb +36 -0
  53. data/data/butler/plugins/irc/quit.rb +36 -0
  54. data/data/butler/plugins/operator/deop.rb +41 -0
  55. data/data/butler/plugins/operator/devoice.rb +41 -0
  56. data/data/butler/plugins/operator/limit.rb +47 -0
  57. data/data/butler/plugins/operator/op.rb +41 -0
  58. data/data/butler/plugins/operator/voice.rb +41 -0
  59. data/data/butler/plugins/public/help.rb +69 -0
  60. data/data/butler/plugins/public/login.rb +72 -0
  61. data/data/butler/plugins/public/usage.rb +49 -0
  62. data/data/butler/plugins/service/clones.rb +56 -0
  63. data/data/butler/plugins/service/define.rb +47 -0
  64. data/data/butler/plugins/service/log.rb +183 -0
  65. data/data/butler/plugins/service/svn.rb +91 -0
  66. data/data/butler/plugins/util/cycle.rb +98 -0
  67. data/data/butler/plugins/util/load.rb +41 -0
  68. data/data/butler/plugins/util/pong.rb +29 -0
  69. data/data/butler/strings/random/acknowledge.en.yaml +5 -0
  70. data/data/butler/strings/random/gratitude.en.yaml +3 -0
  71. data/data/butler/strings/random/hello.en.yaml +4 -0
  72. data/data/butler/strings/random/ignorance.en.yaml +7 -0
  73. data/data/butler/strings/random/ignorance_about.en.yaml +3 -0
  74. data/data/butler/strings/random/insult.en.yaml +3 -0
  75. data/data/butler/strings/random/rejection.en.yaml +12 -0
  76. data/data/man/botcontrol.1 +17 -0
  77. data/lib/access.rb +187 -0
  78. data/lib/access/admin.rb +16 -0
  79. data/lib/access/privilege.rb +122 -0
  80. data/lib/access/role.rb +102 -0
  81. data/lib/access/savable.rb +18 -0
  82. data/lib/access/user.rb +180 -0
  83. data/lib/access/yamlbase.rb +126 -0
  84. data/lib/butler.rb +188 -0
  85. data/lib/butler/bot.rb +247 -0
  86. data/lib/butler/control.rb +93 -0
  87. data/lib/butler/dialog.rb +64 -0
  88. data/lib/butler/initialvalues.rb +40 -0
  89. data/lib/butler/irc/channel.rb +135 -0
  90. data/lib/butler/irc/channels.rb +96 -0
  91. data/lib/butler/irc/client.rb +351 -0
  92. data/lib/butler/irc/hostmask.rb +53 -0
  93. data/lib/butler/irc/message.rb +184 -0
  94. data/lib/butler/irc/parser.rb +125 -0
  95. data/lib/butler/irc/parser/commands.rb +83 -0
  96. data/lib/butler/irc/parser/generic.rb +343 -0
  97. data/lib/butler/irc/socket.rb +378 -0
  98. data/lib/butler/irc/string.rb +186 -0
  99. data/lib/butler/irc/topic.rb +15 -0
  100. data/lib/butler/irc/user.rb +265 -0
  101. data/lib/butler/irc/users.rb +112 -0
  102. data/lib/butler/plugin.rb +249 -0
  103. data/lib/butler/plugin/configproxy.rb +35 -0
  104. data/lib/butler/plugin/mapper.rb +85 -0
  105. data/lib/butler/plugin/matcher.rb +55 -0
  106. data/lib/butler/plugin/onhandlers.rb +70 -0
  107. data/lib/butler/plugin/trigger.rb +58 -0
  108. data/lib/butler/plugins.rb +147 -0
  109. data/lib/butler/version.rb +17 -0
  110. data/lib/cloptions.rb +217 -0
  111. data/lib/cloptions/adapters.rb +24 -0
  112. data/lib/cloptions/switch.rb +132 -0
  113. data/lib/configuration.rb +223 -0
  114. data/lib/dialogline.rb +296 -0
  115. data/lib/dialogline/localizations.rb +24 -0
  116. data/lib/durations.rb +57 -0
  117. data/lib/event.rb +295 -0
  118. data/lib/event/at.rb +64 -0
  119. data/lib/event/every.rb +56 -0
  120. data/lib/event/timed.rb +112 -0
  121. data/lib/installer.rb +75 -0
  122. data/lib/iterator.rb +34 -0
  123. data/lib/log.rb +68 -0
  124. data/lib/log/comfort.rb +85 -0
  125. data/lib/log/converter.rb +23 -0
  126. data/lib/log/entry.rb +152 -0
  127. data/lib/log/fakeio.rb +55 -0
  128. data/lib/log/file.rb +54 -0
  129. data/lib/log/filereader.rb +81 -0
  130. data/lib/log/forward.rb +49 -0
  131. data/lib/log/methods.rb +39 -0
  132. data/lib/log/nolog.rb +18 -0
  133. data/lib/log/splitter.rb +26 -0
  134. data/lib/ostructfixed.rb +26 -0
  135. data/lib/ruby/array/columnize.rb +38 -0
  136. data/lib/ruby/dir/mktree.rb +28 -0
  137. data/lib/ruby/enumerable/join.rb +13 -0
  138. data/lib/ruby/exception/detailed.rb +24 -0
  139. data/lib/ruby/file/append.rb +11 -0
  140. data/lib/ruby/file/write.rb +11 -0
  141. data/lib/ruby/hash/zip.rb +15 -0
  142. data/lib/ruby/kernel/bench.rb +15 -0
  143. data/lib/ruby/kernel/daemonize.rb +42 -0
  144. data/lib/ruby/kernel/non_verbose.rb +17 -0
  145. data/lib/ruby/kernel/safe_fork.rb +18 -0
  146. data/lib/ruby/range/stepped.rb +11 -0
  147. data/lib/ruby/string/arguments.rb +72 -0
  148. data/lib/ruby/string/chunks.rb +15 -0
  149. data/lib/ruby/string/post_arguments.rb +44 -0
  150. data/lib/ruby/string/unescaped.rb +17 -0
  151. data/lib/scheduler.rb +164 -0
  152. data/lib/scriptfile.rb +101 -0
  153. data/lib/templater.rb +86 -0
  154. data/test/cloptions.rb +134 -0
  155. data/test/cv.rb +28 -0
  156. data/test/irc/client.rb +85 -0
  157. data/test/irc/client_login.txt +53 -0
  158. data/test/irc/client_subscribe.txt +8 -0
  159. data/test/irc/message.rb +30 -0
  160. data/test/irc/messages.txt +64 -0
  161. data/test/irc/parser.rb +13 -0
  162. data/test/irc/profile_parser.rb +12 -0
  163. data/test/irc/users.rb +28 -0
  164. metadata +256 -0
@@ -0,0 +1,72 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ #extend OnHandlers
10
+ trigger "login"
11
+
12
+ # FIXME, requires working OnHandlers
13
+ # FIXME, requires Butler::IRC::Message#answer to work with :JOIN
14
+ def on_join
15
+ if @message.from.user[0,1] == "i" then
16
+ # check nick->user mappings
17
+ if user = plugin.config["nickmap"][@message.from.nick] then
18
+ @message.from.
19
+ answer(:success)
20
+ end
21
+ end
22
+ end
23
+
24
+ # FIXME, log events
25
+ # FIXME, accept plain login, using IDENT_CAPAB
26
+ def on_trigger
27
+ return answer(usage) unless [3].include?(@message.arguments.length)
28
+
29
+ if access = @butler.access.login(*@message.arguments[1,2]) then
30
+ @message.from.access = access
31
+ answer(:success)
32
+ if @message.public? then
33
+ @message.from.access.credentials = nil
34
+ answer(:public_success)
35
+ end
36
+ else
37
+ answer(:failure)
38
+ answer(:public_failure) if @message.public?
39
+ end
40
+ end
41
+
42
+ __END__
43
+ ---
44
+ :revision:
45
+ :plugin: 1
46
+ :summary:
47
+ en: Authenticate to the bot
48
+ :about:
49
+ :mail: "apeiros@gmx.net"
50
+ :version: "1.0.0"
51
+ :author: "Stefan Rusterholz"
52
+ :strings:
53
+ :success:
54
+ en: You are now authenticated.
55
+ :failure:
56
+ en: Could not authenticate you.
57
+ :public_success:
58
+ en: |
59
+ You authenticated yourself in public. Your password has been deleted.
60
+ Please set a new one immediatly using 'access password ![c(blue)]password![o]'
61
+ :public_failure:
62
+ en: |
63
+ You tried to authenticate yourself in public. Be aware that a public login will result in a reset password.
64
+ If this was similar to your real password, then please login and set a new one immediatly using 'access password ![(blue)]password![r]'
65
+ :usage:
66
+ en: |
67
+ ![b]login![o] ![c(blue)]username password![o]
68
+ :help:
69
+ en:
70
+ "": |
71
+ Using 'login ![c(blue)]username password![o]' you can Authenticate to the bot.
72
+ Be aware that the bot will delete your password if you authenticate in public as a protection measure.
@@ -0,0 +1,49 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ trigger "usage"
10
+
11
+ def on_trigger
12
+ if @message.arguments.length != 2 then
13
+ answer(usage)
14
+ elsif plugin = @butler.plugins.identify(@message.arguments[1]) then
15
+ answer(@butler.plugins[plugin].plugin_usage(@message))
16
+ else
17
+ answer(:no_plugin, :name => @message.arguments[1])
18
+ end
19
+ end
20
+
21
+ __END__
22
+ ---
23
+ :revision:
24
+ :plugin: 1
25
+ :summary:
26
+ en: Give information about elementary usage of a plugin
27
+ :about:
28
+ :mail: "apeiros@gmx.net"
29
+ :version: "1.0.0"
30
+ :author: "Stefan Rusterholz"
31
+ :strings:
32
+ :no_plugin:
33
+ en: |
34
+ No plugin named <%= name %> loaded.
35
+ :usage:
36
+ en: |
37
+ ![b]usage![o] <![c(green)]plugin![o]>
38
+ :help:
39
+ en:
40
+ "": |
41
+ Usage provides you with elementary information on how to use a plugin-command.
42
+ If you need more information, use the help command.
43
+ "keyword": |
44
+ Arguments in green and embraced by <angle brackets> are keyword arguments.
45
+ Keyword arguments require a specific format.
46
+ "keyword nick": |
47
+ A <nick> argument must be a valid (not necessary online) IRC nickname.
48
+ "keyword channel": |
49
+ A <channel> argument must be a valid (not necessary existing) IRC channelname.
@@ -0,0 +1,56 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ trigger "clones"
10
+
11
+ def on_trigger
12
+ strong = @message.arguments.last != "weak"
13
+ if (@message.arguments.length == 2 && @message.arguments[1].valid_channelname?) then
14
+ all_clones = @butler.channels[message.arguments[1]].clones(strong)
15
+ answer(:clones_in_channel, :channel => message.arguments[1], :count => all_clones.length)
16
+ answer_clones(all_clones)
17
+ else
18
+ all_clones = @butler.users.clones(strong)
19
+ answer(:all_clones, :count => all_clones.length)
20
+ answer_clones(all_clones)
21
+ end
22
+ end
23
+
24
+ def answer_clones(all_clones)
25
+ all_clones.each { |common, clones|
26
+ answer(:clonelist, :common => common, :clones => clones)
27
+ }
28
+ end
29
+
30
+ __END__
31
+ ---
32
+ :revision:
33
+ :plugin: 1
34
+ :summary:
35
+ en: Let butler send a notice to a user or channel
36
+ :about:
37
+ :mail: "apeiros@gmx.net"
38
+ :version: "1.0.0"
39
+ :author: "Stefan Rusterholz"
40
+ :strings:
41
+ :clones_in_channel:
42
+ en: |
43
+ Clones in channel <%= channel %> (<%= count %>):
44
+ :all_clones:
45
+ en: |
46
+ All clones (<%= count %>):
47
+ :clonelist:
48
+ en: |
49
+ <%= common %>: <%= clones.map { |u| "#{u.nick}" }.join(", ") %>
50
+ :usage:
51
+ en: |
52
+ ![b]notice![o] (![c(green)]nick![o] | ![c(green)]channel![o])
53
+ :help:
54
+ en:
55
+ "": |
56
+ Let butler send a notice to a user or channel.
@@ -0,0 +1,47 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'open-uri'
10
+ require 'hpricot'
11
+ require 'cgi'
12
+
13
+ Query = "http://www.google.com/search?client=butler&rls=%s&q=define:+%s&ie=UTF-8&oe=UTF-8"
14
+
15
+ trigger "define"
16
+
17
+ def on_trigger
18
+ word = @message.post_arguments[1]
19
+ doc = Hpricot(open(Query%[@message.language, CGI.escape(word)]))
20
+ see_also = (doc/"//div[@id='res']/font[1]/p/a")[5..-1].map { |e| e.inner_text }
21
+ found = (doc/"//div[@id='res']/ul/font/li").find { |e| (e/"//a").inner_text.include?("wikipedia.org") }
22
+ if found then
23
+ answer(found.inner_text.sub(%r{\w+\.wikipedia\.org/wiki/.*?$}, ''))
24
+ else
25
+ answer(:not_found, :search_string => word)
26
+ end
27
+ end
28
+
29
+ __END__
30
+ ---
31
+ :revision:
32
+ :plugin: 1
33
+ :configuration: 1
34
+ :summary:
35
+ en: "Use googles define: to find the definition of words."
36
+ :about:
37
+ :mail: "apeiros@gmx.net"
38
+ :version: "1.0.0"
39
+ :author: "Stefan Rusterholz"
40
+ :usage:
41
+ en: "![b]define![o] *search_string"
42
+ :help:
43
+ en:
44
+ "": "Use googles define: to find the definition of words."
45
+ :strings:
46
+ :not_found:
47
+ en: No search result for '<%= search_string %>'.
@@ -0,0 +1,183 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'sequel/sqlite'
10
+
11
+ class <<self
12
+ attr_reader :log_db
13
+ attr_reader :log_table
14
+ end
15
+
16
+ @log_db = Sequel.sqlite @butler.path.log+'/log.sqlite'
17
+
18
+ configuration(
19
+ "channels" => []
20
+ )
21
+ @log_db.create_table(:log) {
22
+ primary_key :oid, :integer
23
+ int :time
24
+ str :channel, :size => 255
25
+ str :nick, :size => 48
26
+ int :type
27
+ text :text
28
+ } unless @log_db.table_exists?(:log)
29
+ @log_table = @log_db[:log]
30
+
31
+ MessageTypes = [:PRIVMSG, :NOTICE, :JOIN, :PART, :QUIT, :KICK, :KILL]
32
+
33
+ trigger "log"
34
+
35
+ def on_trigger
36
+ case @message.arguments[1]
37
+ when "list"
38
+ answer(:list, :channels => plugin.config["channels"])
39
+ when "on"
40
+ plugin.config["channels"] |= message.arguments[2..-1]
41
+ answer(:activated, :channels => message.arguments[2..-1])
42
+ when "off"
43
+ plugin.config["channels"] -= message.arguments[2..-1]
44
+ answer(:deactivated, :channels => message.arguments[2..-1])
45
+ when "search"
46
+ text, channel, from = @message.text.match(/search (.*)(?: in channel (#\w+))?(?: from (\w+))?$/).captures
47
+ text = "%#{text}%" if text
48
+ filtered = plugin.log_table #.filter { :type == 0 || :type == 1 }
49
+ filtered = filtered.filter { :nick == from } if from
50
+ filtered = filtered.filter { :nick == channel } if channel
51
+ filtered = filtered.filter { :text =~ text } if text
52
+ count = filtered.count
53
+ if count.zero? then
54
+ answer(:no_match)
55
+ else
56
+ upper = [5,count].min
57
+ answer("Found (1-#{upper} of #{count}):")
58
+ filtered.order(:time.DESC).first(5).each_with_index { |row, index|
59
+ answer(
60
+ "#{index+1}: " \
61
+ "[#{Time.at(row[:time]).strftime('%Y-%m-%d %H:%M')}, " \
62
+ "#{row[:from]} in #{row[:channel]}] " \
63
+ "#{row[:text]}"
64
+ )
65
+ }
66
+ end
67
+ when "dump"
68
+ n = @message.arguments[2].to_i
69
+ n = 5 if n.zero?
70
+ data = plugin.log_table.order(:time.DESC).first(n)
71
+ answer("Dump(#{data.length}):")
72
+ data.each_with_index { |row, index|
73
+ answer(
74
+ "#{index+1}: " \
75
+ "[#{Time.at(row[:time]).strftime('%Y-%m-%d %H:%M')}, " \
76
+ "#{row[:from]} in #{row[:channel]}] " \
77
+ "#{row[:text]}"
78
+ )
79
+ }
80
+ else
81
+ answer(usage)
82
+ end
83
+ end
84
+
85
+ subscribe(:PRIVMSG, -10, nil, MessageTypes.index(:PRIVMSG)) { |listener, message, type|
86
+ next unless @config["channels"].include?(message.channel.to_str)
87
+ @log_table << {
88
+ :time => Time.now.to_i,
89
+ :channel => message.channel.to_str,
90
+ :nick => message.from.nick,
91
+ :type => type,
92
+ :text => message.text
93
+ }
94
+ }
95
+ subscribe(:NOTICE, -10, nil, MessageTypes.index(:NOTICE)) { |listener, message, type|
96
+ next unless @config["channels"].include?(message.channel.to_str)
97
+ @log_table << {
98
+ :time => Time.now.to_i,
99
+ :channel => message.channel.to_str,
100
+ :nick => message.from.nick,
101
+ :type => type,
102
+ :text => message.text
103
+ }
104
+ }
105
+ subscribe(:JOIN, -10, nil, MessageTypes.index(:JOIN)) { |listener, message, type|
106
+ next unless @config["channels"].include?(message.channel.to_str)
107
+ @log_table << {
108
+ :time => Time.now.to_i,
109
+ :channel => message.channel.to_str,
110
+ :nick => message.from.nick,
111
+ :type => type,
112
+ :text => nil
113
+ }
114
+ }
115
+ subscribe(:PART, -10, nil, MessageTypes.index(:PART)) { |listener, message, type|
116
+ next unless @config["channels"].include?(message.channel.to_str)
117
+ @log_table << {
118
+ :time => Time.now.to_i,
119
+ :channel => message.channel.to_str,
120
+ :nick => message.from.nick,
121
+ :type => type,
122
+ :text => message.text
123
+ }
124
+ }
125
+ subscribe(:QUIT, -10, nil, MessageTypes.index(:QUIT)) { |listener, message, type|
126
+ next unless @config["channels"].include?(message.channel.to_str)
127
+ @log_table << {
128
+ :time => Time.now.to_i,
129
+ :channel => message.channel.to_str,
130
+ :nick => message.from.nick,
131
+ :type => type,
132
+ :text => message.text
133
+ }
134
+ }
135
+ subscribe(:KICK, -10, nil, MessageTypes.index(:KICK)) { |listener, message, type|
136
+ next unless @config["channels"].include?(message.channel.to_str)
137
+ @log_table << {
138
+ :time => Time.now.to_i,
139
+ :channel => message.channel.to_str,
140
+ :nick => message.from.nick,
141
+ :type => type,
142
+ :text => message.text
143
+ }
144
+ }
145
+ subscribe(:KILL, -10, nil, MessageTypes.index(:KILL)) { |listener, message, type|
146
+ next unless @config["channels"].include?(message.channel.to_str)
147
+ @log_table << {
148
+ :time => Time.now.to_i,
149
+ :channel => message.channel.to_str,
150
+ :nick => message.from.nick,
151
+ :type => type,
152
+ :text => message.text
153
+ }
154
+ }
155
+
156
+ __END__
157
+ ---
158
+ :revision:
159
+ :plugin: 1
160
+ :configuration: 1
161
+ :summary:
162
+ en: Log channel messages
163
+ :about:
164
+ :mail: "apeiros@gmx.net"
165
+ :version: "1.0.0"
166
+ :author: "Stefan Rusterholz"
167
+ :usage:
168
+ en: "![b]log![o] 'list'"
169
+ :help:
170
+ en:
171
+ "": Log channel messages.
172
+ :strings:
173
+ :no_match:
174
+ en: "No match found."
175
+ :list:
176
+ en: |
177
+ Logging is active for: <%= channels.join(', ') %>.
178
+ :activated:
179
+ en: |
180
+ Logging activated in: <%= channels.join(', ') %>.
181
+ :deactivated:
182
+ en: |
183
+ Logging deactivated in: <%= channels.join(', ') %>.
@@ -0,0 +1,91 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'open-uri'
10
+ require 'hpricot'
11
+ require 'cgi'
12
+
13
+ configuration(
14
+ "frequency" => 900,
15
+ "repositories" => {}
16
+ )
17
+
18
+ def self.current_revision(repo)
19
+ (Hpricot.XML(`svn info --xml #{repo}`)/"info/entry").first.attributes["revision"].to_i
20
+ end
21
+
22
+ def self.changes(repo, from, to=nil)
23
+ range = to ? "#{from}:#{to}" : from.to_s
24
+ doc = Hpricot.XML(`svn log -r #{range} --xml #{repo}`)
25
+ (doc/"log/logentry").map { |entry|
26
+ "#{(entry/'author').inner_text}, " \
27
+ "r#{entry.attributes['revision']}: " \
28
+ "#{(entry/'msg').inner_text.gsub("\n", ' ')}"
29
+ }
30
+ end
31
+
32
+ every(@config["frequency"]) do
33
+ repositories = @config["repositories"]
34
+ repositories.each { |repo, data|
35
+ cr, lr = current_revision(repo), data[:revision]
36
+ if cr > lr then
37
+ data[:revision] = cr
38
+ @config["repositories"] = repositories
39
+ changed = changes(repo, lr+1, cr).join("\n")
40
+ @butler.irc.privmsg(changed, *data[:channels])
41
+ end
42
+ }
43
+ end
44
+
45
+ trigger "svn"
46
+
47
+ def on_trigger
48
+ repos = plugin.config["repositories"]
49
+ case arguments(1)
50
+ when "add"
51
+ repo = @message.arguments[2]
52
+ repos[repo] = {
53
+ :revision => plugin.current_revision(repo),
54
+ :channels => @message.arguments[3..-1]
55
+ }
56
+ plugin.config["repositories"] = repos
57
+ answer(:added, :repository => repo)
58
+
59
+ when "remove"
60
+ repo = @message.arguments[2]
61
+ repos.delete(repo)
62
+ plugin.config["repositories"] = repos
63
+ answer(:removed, :repository => repo)
64
+
65
+ else
66
+ answer(usage)
67
+ end
68
+ #
69
+ end
70
+
71
+ __END__
72
+ ---
73
+ :revision:
74
+ :plugin: 1
75
+ :configuration: 1
76
+ :summary:
77
+ en: "Poll repositories for changes and publicate them."
78
+ :about:
79
+ :mail: "apeiros@gmx.net"
80
+ :version: "1.0.0"
81
+ :author: "Stefan Rusterholz"
82
+ :usage:
83
+ en: "![b]svn![o] 'add' repository channel ..."
84
+ :help:
85
+ en:
86
+ "": "Poll repositories for changes and publicate them."
87
+ :strings:
88
+ :added:
89
+ en: Added repository <%= repository %>.
90
+ :removed:
91
+ en: Added repository <%= repository %>.