rbot 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (368) hide show
  1. data/AUTHORS +6 -2
  2. data/REQUIREMENTS +7 -1
  3. data/Rakefile +10 -32
  4. data/bin/rbot +6 -1
  5. data/bin/svnwatch-postcommit-hook +68 -0
  6. data/data/rbot/contrib/plugins/stats.rb +3 -3
  7. data/data/rbot/contrib/plugins/vandale.rb +1 -1
  8. data/data/rbot/filters/rss.rb +72 -0
  9. data/data/rbot/languages/finnish.lang +50 -0
  10. data/data/rbot/plugins/alias.rb +6 -6
  11. data/data/rbot/plugins/autorejoin.rb +41 -2
  12. data/data/rbot/plugins/bans.rb +100 -6
  13. data/data/rbot/plugins/bash.rb +9 -4
  14. data/data/rbot/plugins/cal.rb +1 -1
  15. data/data/rbot/plugins/chucknorris.rb +6 -6
  16. data/data/rbot/plugins/debugger.rb +7 -3
  17. data/data/rbot/plugins/deepthoughts.rb +1 -1
  18. data/data/rbot/plugins/delicious.rb +6 -2
  19. data/data/rbot/plugins/dice.rb +7 -7
  20. data/data/rbot/plugins/dict.rb +4 -3
  21. data/data/rbot/plugins/dictclient.rb +17 -13
  22. data/data/rbot/plugins/digg.rb +3 -3
  23. data/data/rbot/plugins/eightball.rb +1 -1
  24. data/data/rbot/plugins/factoids.rb +13 -4
  25. data/data/rbot/plugins/figlet.rb +4 -4
  26. data/data/rbot/plugins/forecast.rb +3 -3
  27. data/data/rbot/plugins/fortune.rb +14 -8
  28. data/data/rbot/plugins/freshmeat.rb +2 -2
  29. data/data/rbot/plugins/games/azgame.rb +72 -19
  30. data/data/rbot/plugins/games/hangman.rb +499 -0
  31. data/data/rbot/plugins/games/quiz.rb +15 -13
  32. data/data/rbot/plugins/games/roshambo.rb +1 -1
  33. data/data/rbot/plugins/games/roulette.rb +4 -4
  34. data/data/rbot/plugins/games/shiritori.rb +31 -31
  35. data/data/rbot/plugins/games/uno.rb +28 -6
  36. data/data/rbot/plugins/games/wheelfortune.rb +1 -3
  37. data/data/rbot/plugins/geoip.rb +83 -28
  38. data/data/rbot/plugins/googlefight.rb +64 -0
  39. data/data/rbot/plugins/greet.rb +45 -0
  40. data/data/rbot/plugins/grouphug.rb +40 -12
  41. data/data/rbot/plugins/imdb.rb +4 -4
  42. data/data/rbot/plugins/insult.rb +2 -2
  43. data/data/rbot/plugins/karma.rb +6 -5
  44. data/data/rbot/plugins/keywords.rb +26 -22
  45. data/data/rbot/plugins/lart.rb +5 -6
  46. data/data/rbot/plugins/lastfm.rb +488 -125
  47. data/data/rbot/plugins/lib_spotify.rb +84 -0
  48. data/data/rbot/plugins/linkbot.rb +1 -1
  49. data/data/rbot/plugins/markov.rb +567 -78
  50. data/data/rbot/plugins/math.rb +3 -3
  51. data/data/rbot/plugins/modes.rb +1 -1
  52. data/data/rbot/plugins/nickrecover.rb +1 -1
  53. data/data/rbot/plugins/nickserv.rb +7 -7
  54. data/data/rbot/plugins/note.rb +55 -0
  55. data/data/rbot/plugins/nslookup.rb +2 -2
  56. data/data/rbot/plugins/quakeauth.rb +4 -4
  57. data/data/rbot/plugins/quotes.rb +53 -19
  58. data/data/rbot/plugins/reaction.rb +76 -19
  59. data/data/rbot/plugins/remind.rb +3 -96
  60. data/data/rbot/plugins/ri.rb +1 -1
  61. data/data/rbot/plugins/rot13.rb +1 -1
  62. data/data/rbot/plugins/rss.rb +296 -190
  63. data/data/rbot/plugins/salut.rb +8 -8
  64. data/data/rbot/plugins/script.rb +48 -11
  65. data/data/rbot/plugins/search.rb +124 -28
  66. data/data/rbot/plugins/seen.rb +162 -31
  67. data/data/rbot/plugins/shortenurls.rb +1 -1
  68. data/data/rbot/plugins/slashdot.rb +19 -6
  69. data/data/rbot/plugins/spotify.rb +78 -0
  70. data/data/rbot/plugins/theyfightcrime.rb +10 -10
  71. data/data/rbot/plugins/time.rb +2 -2
  72. data/data/rbot/plugins/translator.rb +161 -85
  73. data/data/rbot/plugins/tube.rb +2 -2
  74. data/data/rbot/plugins/tumblr.rb +143 -0
  75. data/data/rbot/plugins/twitter.rb +25 -6
  76. data/data/rbot/plugins/urban.rb +6 -4
  77. data/data/rbot/plugins/url.rb +49 -10
  78. data/data/rbot/plugins/weather.rb +6 -6
  79. data/data/rbot/plugins/wserver.rb +5 -5
  80. data/data/rbot/plugins/youtube.rb +12 -12
  81. data/data/rbot/templates/lart/larts-italian +1 -1
  82. data/launch_here.rb +68 -0
  83. data/lib/rbot/botuser.rb +1 -1
  84. data/lib/rbot/compat19.rb +70 -0
  85. data/lib/rbot/config.rb +8 -6
  86. data/lib/rbot/core/auth.rb +37 -21
  87. data/lib/rbot/core/basics.rb +33 -2
  88. data/lib/rbot/core/config.rb +24 -17
  89. data/lib/rbot/core/filters_ui.rb +2 -2
  90. data/lib/rbot/core/irclog.rb +20 -11
  91. data/lib/rbot/core/remote.rb +9 -9
  92. data/lib/rbot/core/unicode.rb +4 -0
  93. data/lib/rbot/core/userdata.rb +16 -1
  94. data/lib/rbot/core/utils/extends.rb +76 -0
  95. data/lib/rbot/core/utils/filters.rb +47 -0
  96. data/lib/rbot/core/utils/httputil.rb +36 -26
  97. data/lib/rbot/core/utils/parse_time.rb +193 -0
  98. data/lib/rbot/core/utils/utils.rb +81 -56
  99. data/lib/rbot/core/utils/wordlist.rb +66 -0
  100. data/lib/rbot/core/wordlist_ui.rb +27 -0
  101. data/lib/rbot/irc.rb +59 -19
  102. data/lib/rbot/ircbot.rb +190 -58
  103. data/lib/rbot/ircsocket.rb +14 -8
  104. data/lib/rbot/language.rb +4 -3
  105. data/lib/rbot/load-gettext.rb +22 -9
  106. data/lib/rbot/message.rb +89 -18
  107. data/lib/rbot/messagemapper.rb +71 -19
  108. data/lib/rbot/plugins.rb +112 -44
  109. data/lib/rbot/{registry.rb → registry/bdb.rb} +226 -22
  110. data/lib/rbot/registry/tc.rb +531 -0
  111. data/lib/rbot/rfc2812.rb +33 -8
  112. data/lib/rbot/timer.rb +12 -20
  113. data/po/en_US/rbot-autorejoin.po +3 -0
  114. data/po/en_US/rbot-azgame.po +51 -43
  115. data/po/en_US/rbot-bash.po +15 -0
  116. data/po/en_US/rbot-dictclient.po +20 -20
  117. data/po/en_US/rbot-factoids.po +9 -9
  118. data/po/en_US/rbot-geoip.po +0 -0
  119. data/po/en_US/rbot-googlefight.po +24 -0
  120. data/po/en_US/rbot-grouphug.po +4 -4
  121. data/po/en_US/rbot-hangman.po +114 -0
  122. data/po/en_US/rbot-keywords.po +3 -3
  123. data/po/en_US/rbot-lastfm.po +268 -70
  124. data/po/en_US/rbot-markov.po +73 -2
  125. data/po/en_US/rbot-quotes.po +21 -21
  126. data/po/en_US/rbot-rss.po +6 -2
  127. data/po/en_US/rbot-script.po +3 -0
  128. data/po/en_US/rbot-seen.po +72 -0
  129. data/po/en_US/rbot-spell.po +2 -2
  130. data/po/en_US/rbot-translator.po +13 -13
  131. data/po/en_US/rbot-twitter.po +3 -3
  132. data/po/en_US/rbot-uno.po +131 -114
  133. data/po/en_US/rbot-wall.po +12 -13
  134. data/po/en_US/rbot-wheelfortune.po +41 -41
  135. data/po/en_US/rbot.po +254 -194
  136. data/po/fi/rbot-alias.po +82 -0
  137. data/po/fi/rbot-autoop.po +0 -0
  138. data/po/fi/rbot-autorejoin.po +20 -0
  139. data/po/fi/rbot-azgame.po +194 -0
  140. data/po/fi/rbot-bans.po +0 -0
  141. data/po/fi/rbot-bash.po +32 -0
  142. data/po/fi/rbot-botsnack.po +0 -0
  143. data/po/fi/rbot-cal.po +20 -0
  144. data/po/fi/rbot-chanserv.po +0 -0
  145. data/po/fi/rbot-chucknorris.po +0 -0
  146. data/po/fi/rbot-debugger.po +0 -0
  147. data/po/fi/rbot-deepthoughts.po +0 -0
  148. data/po/fi/rbot-delicious.po +0 -0
  149. data/po/fi/rbot-dice.po +0 -0
  150. data/po/fi/rbot-dict.po +0 -0
  151. data/po/fi/rbot-dictclient.po +111 -0
  152. data/po/fi/rbot-digg.po +0 -0
  153. data/po/fi/rbot-eightball.po +0 -0
  154. data/po/fi/rbot-excuse.po +0 -0
  155. data/po/fi/rbot-factoids.po +107 -0
  156. data/po/fi/rbot-figlet.po +36 -0
  157. data/po/fi/rbot-fish.po +0 -0
  158. data/po/fi/rbot-forecast.po +0 -0
  159. data/po/fi/rbot-fortune.po +0 -0
  160. data/po/fi/rbot-freshmeat.po +0 -0
  161. data/po/fi/rbot-geoip.po +0 -0
  162. data/po/fi/rbot-googlefight.po +24 -0
  163. data/po/fi/rbot-grouphug.po +35 -0
  164. data/po/fi/rbot-hangman.po +121 -0
  165. data/po/fi/rbot-hl2.po +0 -0
  166. data/po/fi/rbot-host.po +20 -0
  167. data/po/fi/rbot-imdb.po +0 -0
  168. data/po/fi/rbot-insult.po +0 -0
  169. data/po/fi/rbot-iplookup.po +0 -0
  170. data/po/fi/rbot-karma.po +0 -0
  171. data/po/fi/rbot-keywords.po +24 -0
  172. data/po/fi/rbot-lart.po +0 -0
  173. data/po/fi/rbot-lastfm.po +377 -0
  174. data/po/fi/rbot-linkbot.po +0 -0
  175. data/po/fi/rbot-markov.po +91 -0
  176. data/po/fi/rbot-math.po +0 -0
  177. data/po/fi/rbot-modes.po +0 -0
  178. data/po/fi/rbot-nickrecover.po +36 -0
  179. data/po/fi/rbot-nickserv.po +104 -0
  180. data/po/fi/rbot-nslookup.po +0 -0
  181. data/po/fi/rbot-quakeauth.po +0 -0
  182. data/po/fi/rbot-quiz.po +0 -0
  183. data/po/fi/rbot-quotes.po +108 -0
  184. data/po/fi/rbot-reaction.po +0 -0
  185. data/po/fi/rbot-remind.po +0 -0
  186. data/po/fi/rbot-remotectl.po +0 -0
  187. data/po/fi/rbot-ri.po +0 -0
  188. data/po/fi/rbot-roshambo.po +0 -0
  189. data/po/fi/rbot-rot13.po +0 -0
  190. data/po/fi/rbot-roulette.po +0 -0
  191. data/po/fi/rbot-rss.po +24 -0
  192. data/po/fi/rbot-salut.po +0 -0
  193. data/po/fi/rbot-script.po +20 -0
  194. data/po/fi/rbot-search.po +0 -0
  195. data/po/fi/rbot-seen.po +92 -0
  196. data/po/fi/rbot-shiritori.po +102 -0
  197. data/po/fi/rbot-shortenurls.po +0 -0
  198. data/po/fi/rbot-slashdot.po +0 -0
  199. data/po/fi/rbot-spell.po +54 -0
  200. data/po/fi/rbot-theyfightcrime.po +0 -0
  201. data/po/fi/rbot-threat.po +0 -0
  202. data/po/fi/rbot-time.po +0 -0
  203. data/po/fi/rbot-topic.po +0 -0
  204. data/po/fi/rbot-translator.po +77 -0
  205. data/po/fi/rbot-tube.po +0 -0
  206. data/po/fi/rbot-twitter.po +24 -0
  207. data/po/fi/rbot-uno.po +529 -0
  208. data/po/fi/rbot-urban.po +0 -0
  209. data/po/fi/rbot-url.po +0 -0
  210. data/po/fi/rbot-usermodes.po +0 -0
  211. data/po/fi/rbot-wall.po +32 -0
  212. data/po/fi/rbot-weather.po +0 -0
  213. data/po/fi/rbot-wheelfortune.po +205 -0
  214. data/po/fi/rbot-wow.po +0 -0
  215. data/po/fi/rbot-wserver.po +0 -0
  216. data/po/fi/rbot-youtube.po +58 -0
  217. data/po/fi/rbot.po +1152 -0
  218. data/po/fr/rbot-autorejoin.po +3 -0
  219. data/po/fr/rbot-azgame.po +51 -43
  220. data/po/fr/rbot-bash.po +15 -0
  221. data/po/fr/rbot-dictclient.po +20 -20
  222. data/po/fr/rbot-factoids.po +9 -9
  223. data/po/fr/rbot-geoip.po +0 -0
  224. data/po/fr/rbot-googlefight.po +24 -0
  225. data/po/fr/rbot-grouphug.po +4 -4
  226. data/po/fr/rbot-hangman.po +114 -0
  227. data/po/fr/rbot-keywords.po +3 -3
  228. data/po/fr/rbot-lastfm.po +268 -70
  229. data/po/fr/rbot-markov.po +74 -2
  230. data/po/fr/rbot-quotes.po +21 -21
  231. data/po/fr/rbot-rss.po +6 -2
  232. data/po/fr/rbot-script.po +3 -0
  233. data/po/fr/rbot-seen.po +72 -0
  234. data/po/fr/rbot-spell.po +2 -2
  235. data/po/fr/rbot-translator.po +13 -13
  236. data/po/fr/rbot-twitter.po +3 -3
  237. data/po/fr/rbot-uno.po +132 -114
  238. data/po/fr/rbot-wall.po +8 -9
  239. data/po/fr/rbot-wheelfortune.po +41 -41
  240. data/po/fr/rbot.po +268 -197
  241. data/po/it/rbot-autorejoin.po +3 -0
  242. data/po/it/rbot-azgame.po +50 -42
  243. data/po/it/rbot-bash.po +15 -0
  244. data/po/it/rbot-dictclient.po +20 -20
  245. data/po/it/rbot-factoids.po +9 -9
  246. data/po/it/rbot-geoip.po +0 -0
  247. data/po/it/rbot-googlefight.po +24 -0
  248. data/po/it/rbot-grouphug.po +4 -4
  249. data/po/it/rbot-hangman.po +114 -0
  250. data/po/it/rbot-keywords.po +3 -3
  251. data/po/it/rbot-lastfm.po +268 -70
  252. data/po/it/rbot-markov.po +75 -3
  253. data/po/it/rbot-quotes.po +21 -21
  254. data/po/it/rbot-rss.po +7 -3
  255. data/po/it/rbot-script.po +19 -0
  256. data/po/it/rbot-seen.po +72 -0
  257. data/po/it/rbot-spell.po +2 -2
  258. data/po/it/rbot-translator.po +13 -13
  259. data/po/it/rbot-twitter.po +3 -3
  260. data/po/it/rbot-uno.po +137 -116
  261. data/po/it/rbot-wall.po +8 -9
  262. data/po/it/rbot-wheelfortune.po +41 -41
  263. data/po/it/rbot.po +265 -208
  264. data/po/ja/rbot-autorejoin.po +3 -0
  265. data/po/ja/rbot-azgame.po +51 -43
  266. data/po/ja/rbot-bash.po +15 -0
  267. data/po/ja/rbot-dictclient.po +20 -20
  268. data/po/ja/rbot-factoids.po +9 -9
  269. data/po/ja/rbot-geoip.po +0 -0
  270. data/po/ja/rbot-googlefight.po +24 -0
  271. data/po/ja/rbot-grouphug.po +4 -4
  272. data/po/ja/rbot-hangman.po +114 -0
  273. data/po/ja/rbot-keywords.po +3 -3
  274. data/po/ja/rbot-lastfm.po +268 -70
  275. data/po/ja/rbot-markov.po +73 -2
  276. data/po/ja/rbot-quotes.po +21 -21
  277. data/po/ja/rbot-rss.po +6 -2
  278. data/po/ja/rbot-script.po +3 -0
  279. data/po/ja/rbot-seen.po +72 -0
  280. data/po/ja/rbot-spell.po +2 -2
  281. data/po/ja/rbot-translator.po +13 -13
  282. data/po/ja/rbot-twitter.po +3 -3
  283. data/po/ja/rbot-uno.po +131 -114
  284. data/po/ja/rbot-wall.po +8 -9
  285. data/po/ja/rbot-wheelfortune.po +41 -41
  286. data/po/ja/rbot.po +248 -192
  287. data/po/rbot-alias.pot +2 -2
  288. data/po/rbot-autorejoin.pot +21 -0
  289. data/po/rbot-azgame.pot +51 -43
  290. data/po/rbot-bash.pot +33 -0
  291. data/po/rbot-cal.pot +2 -2
  292. data/po/rbot-dictclient.pot +21 -21
  293. data/po/rbot-factoids.pot +10 -10
  294. data/po/rbot-figlet.pot +2 -2
  295. data/po/rbot-geoip.pot +0 -0
  296. data/po/rbot-googlefight.pot +25 -0
  297. data/po/rbot-grouphug.pot +6 -6
  298. data/po/rbot-hangman.pot +115 -0
  299. data/po/rbot-host.pot +2 -2
  300. data/po/rbot-keywords.pot +4 -4
  301. data/po/rbot-lastfm.pot +270 -72
  302. data/po/rbot-markov.pot +74 -3
  303. data/po/rbot-nickrecover.pot +2 -2
  304. data/po/rbot-nickserv.pot +2 -2
  305. data/po/rbot-quotes.pot +22 -22
  306. data/po/rbot-rss.pot +7 -3
  307. data/po/rbot-script.pot +21 -0
  308. data/po/rbot-seen.pot +90 -0
  309. data/po/rbot-shiritori.pot +2 -2
  310. data/po/rbot-spell.pot +3 -3
  311. data/po/rbot-translator.pot +14 -14
  312. data/po/rbot-twitter.pot +4 -4
  313. data/po/rbot-uno.pot +132 -115
  314. data/po/rbot-wall.pot +2 -2
  315. data/po/rbot-wheelfortune.pot +42 -42
  316. data/po/rbot-youtube.pot +2 -2
  317. data/po/rbot.pot +249 -193
  318. data/po/zh_CN/rbot-autorejoin.po +3 -0
  319. data/po/zh_CN/rbot-azgame.po +50 -42
  320. data/po/zh_CN/rbot-bash.po +15 -0
  321. data/po/zh_CN/rbot-dictclient.po +20 -20
  322. data/po/zh_CN/rbot-factoids.po +9 -9
  323. data/po/zh_CN/rbot-geoip.po +0 -0
  324. data/po/zh_CN/rbot-googlefight.po +24 -0
  325. data/po/zh_CN/rbot-grouphug.po +4 -4
  326. data/po/zh_CN/rbot-hangman.po +114 -0
  327. data/po/zh_CN/rbot-keywords.po +3 -3
  328. data/po/zh_CN/rbot-lastfm.po +268 -70
  329. data/po/zh_CN/rbot-markov.po +73 -2
  330. data/po/zh_CN/rbot-quotes.po +21 -21
  331. data/po/zh_CN/rbot-rss.po +6 -2
  332. data/po/zh_CN/rbot-script.po +3 -0
  333. data/po/zh_CN/rbot-seen.po +72 -0
  334. data/po/zh_CN/rbot-spell.po +2 -2
  335. data/po/zh_CN/rbot-translator.po +13 -13
  336. data/po/zh_CN/rbot-twitter.po +3 -3
  337. data/po/zh_CN/rbot-uno.po +131 -114
  338. data/po/zh_CN/rbot-wall.po +7 -8
  339. data/po/zh_CN/rbot-wheelfortune.po +41 -41
  340. data/po/zh_CN/rbot.po +248 -192
  341. data/po/zh_TW/rbot-autorejoin.po +3 -0
  342. data/po/zh_TW/rbot-azgame.po +50 -42
  343. data/po/zh_TW/rbot-bash.po +15 -0
  344. data/po/zh_TW/rbot-dictclient.po +20 -20
  345. data/po/zh_TW/rbot-factoids.po +9 -9
  346. data/po/zh_TW/rbot-geoip.po +0 -0
  347. data/po/zh_TW/rbot-googlefight.po +24 -0
  348. data/po/zh_TW/rbot-grouphug.po +4 -4
  349. data/po/zh_TW/rbot-hangman.po +114 -0
  350. data/po/zh_TW/rbot-keywords.po +3 -3
  351. data/po/zh_TW/rbot-lastfm.po +268 -70
  352. data/po/zh_TW/rbot-markov.po +73 -2
  353. data/po/zh_TW/rbot-quotes.po +21 -21
  354. data/po/zh_TW/rbot-rss.po +6 -2
  355. data/po/zh_TW/rbot-script.po +3 -0
  356. data/po/zh_TW/rbot-seen.po +72 -0
  357. data/po/zh_TW/rbot-spell.po +2 -2
  358. data/po/zh_TW/rbot-translator.po +13 -13
  359. data/po/zh_TW/rbot-twitter.po +3 -3
  360. data/po/zh_TW/rbot-uno.po +131 -114
  361. data/po/zh_TW/rbot-wall.po +7 -8
  362. data/po/zh_TW/rbot-wheelfortune.po +41 -41
  363. data/po/zh_TW/rbot.po +253 -194
  364. data/setup.rb +4 -4
  365. metadata +127 -18
  366. data/README +0 -43
  367. data/data/rbot/plugins/fish.rb +0 -121
  368. data/lib/rbot/dbhash.rb +0 -199
@@ -44,7 +44,7 @@ class MathPlugin < Plugin
44
44
  val = Math.exp($2).to_s
45
45
  expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
46
46
  end
47
-
47
+
48
48
  while expr =~ /^\s*(dec2hex\s*(\d+))\s*\?*/
49
49
  exp = $1
50
50
  val = sprintf("%x", $2)
@@ -56,13 +56,13 @@ class MathPlugin < Plugin
56
56
  while expr =~ /(log\s*((\d+\.?\d*)|\d*\.?\d+))\s*/
57
57
  exp = $1
58
58
  res = $2
59
-
59
+
60
60
  if res == 0
61
61
  val = "Infinity"
62
62
  else
63
63
  val = Math.log(res).to_s
64
64
  end
65
-
65
+
66
66
  expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
67
67
  end
68
68
 
@@ -73,7 +73,7 @@ class ModesPlugin < Plugin
73
73
  def do_mode(m, channel, user, mode)
74
74
  unless channel
75
75
  if m.private?
76
- target = user.nil? ? "you" : user
76
+ target = user.nil? ? "you" : user
77
77
  m.reply "You should tell me where you want me to #{mode} #{target}."
78
78
  return
79
79
  else
@@ -11,7 +11,7 @@
11
11
  # in case it couldn't be achieved.
12
12
 
13
13
  class NickRecoverPlugin < Plugin
14
-
14
+
15
15
  Config.register Config::IntegerValue.new('irc.nick_retry',
16
16
  :default => 60, :valiedate => Proc.new { |v| v >= 0 },
17
17
  :on_change => Proc.new do |bot, v|
@@ -20,7 +20,7 @@
20
20
  # previously identified successfully
21
21
 
22
22
  class NickServPlugin < Plugin
23
-
23
+
24
24
  Config.register Config::StringValue.new('nickserv.name',
25
25
  :default => "nickserv", :requires_restart => false,
26
26
  :desc => _("Name of the nick server (all lowercase)"))
@@ -30,12 +30,12 @@ class NickServPlugin < Plugin
30
30
  :on_change => Proc.new { |bot, v| bot.plugins.delegate "set_ident_request", v },
31
31
  :desc => _("String to look for to see if the nick server is asking us to identify"))
32
32
  Config.register Config::StringValue.new('nickserv.nick_avail',
33
- :default => "not (currently )?online|killed|recovered|disconnesso|libero",
33
+ :default => "not (currently )?online|killed|ghosted|recovered|disconnesso|libero",
34
34
  :requires_restart => false,
35
35
  :on_change => Proc.new { |bot, v| bot.plugins.delegate "set_nick_avail", v },
36
36
  :desc => _("String to look for to see if the nick server is informing us that our nick is now available"))
37
37
  Config.register Config::StringValue.new('nickserv.identified_string',
38
- :default => "((Password|Contrase|Mot de passe).+(acce[pt]t|r[ie]cog?n).+)?(you|tu|vous).+(identif|r[ie]cog?n)",
38
+ :default => "((Password|Contrase|Mot de passe).+(acce[pt]t|r[ie]cog?n).+)?(you|tu|vous|now|adesso).+(identif|r[ie]cog?n)",
39
39
  :requires_restart => false,
40
40
  :on_change => Proc.new { |bot, v| bot.plugins.delegate "set_identified_string", v },
41
41
  :desc => _("String to look for to see if the nick server is informing us that we have identified successfully"))
@@ -51,7 +51,7 @@ class NickServPlugin < Plugin
51
51
  def help(plugin, topic="")
52
52
  case topic
53
53
  when ""
54
- return _("nickserv plugin: handles nickserv protected IRC nicks. topics password, register, identify, listnicks")
54
+ return _("nickserv plugin: handles nickserv protected IRC nicks. topics: password, register, identify, listnicks")
55
55
  when "password"
56
56
  return _("nickserv password [<nick>] <passwd>: remember the password for nick <nick> and use it to identify in future")
57
57
  when "register"
@@ -62,7 +62,7 @@ class NickServPlugin < Plugin
62
62
  return _("nickserv listnicks: lists nicknames and associated password the bot knows about - you will need config level auth access to do this one and it will reply by privmsg only")
63
63
  end
64
64
  end
65
-
65
+
66
66
  def genpasswd
67
67
  return Irc::Bot::Auth.random_password
68
68
  end
@@ -167,12 +167,12 @@ class NickServPlugin < Plugin
167
167
  m.reply _("uh ... something went wrong ...")
168
168
  end
169
169
  end
170
-
170
+
171
171
  def connect
172
172
  @identified = false
173
173
  do_identify
174
174
  end
175
-
175
+
176
176
  def nicktaken(nick)
177
177
  if @registry.has_key?(nick)
178
178
  ns_say "GHOST #{nick} #{@registry[nick]}"
@@ -0,0 +1,55 @@
1
+ #++
2
+ #
3
+ # :title: Note plugin for rbot
4
+ #
5
+ # Author:: dmitry kim <dmitry dot kim at gmail dot com>
6
+ #
7
+ # Copyright:: (C) 200?-2009 dmitry 'jsn' kim
8
+ #
9
+ # License:: MIT license
10
+
11
+ class NotePlugin < Plugin
12
+ Note = Struct.new('Note', :time, :from, :private, :text)
13
+
14
+ def help(plugin, topic="")
15
+ "note <nick> <string> => stores a note (<string>) for <nick>"
16
+ end
17
+
18
+ def message(m)
19
+ begin
20
+ return unless @registry.has_key? m.sourcenick
21
+ pub = []
22
+ priv = []
23
+ @registry[m.sourcenick].each do |n|
24
+ s = "[#{n.time.strftime('%H:%M')}] <#{n.from}> #{n.text}"
25
+ (n.private ? priv : pub).push s
26
+ end
27
+ if !pub.empty?
28
+ @bot.say m.replyto, "#{m.sourcenick}, you have notes! " +
29
+ pub.join(' ')
30
+ end
31
+
32
+ if !priv.empty?
33
+ @bot.say m.sourcenick, "you have notes! " + priv.join(' ')
34
+ end
35
+ @registry.delete m.sourcenick
36
+ rescue Exception => e
37
+ m.reply e.message
38
+ end
39
+ end
40
+
41
+ def note(m, params)
42
+ begin
43
+ q = @registry[params[:nick]] || Array.new
44
+ s = params[:string].to_s.strip
45
+ raise 'cowardly discarding the empty note' if s.empty?
46
+ q.push Note.new(Time.now, m.sourcenick, m.private?, s)
47
+ @registry[params[:nick]] = q
48
+ m.okay
49
+ rescue Exception => e
50
+ m.reply "error: #{e.message}"
51
+ end
52
+ end
53
+ end
54
+
55
+ NotePlugin.new.map 'note :nick *string'
@@ -10,7 +10,7 @@ class DnsPlugin < Plugin
10
10
  def help(plugin, topic="")
11
11
  "dns <hostname|ip> => show local resolution results for hostname or ip address"
12
12
  end
13
-
13
+
14
14
  def name_to_ip(m, params)
15
15
  begin
16
16
  a = getaddresses(params[:host])
@@ -23,7 +23,7 @@ class DnsPlugin < Plugin
23
23
  m.reply "#{params[:host]}: not found"
24
24
  end
25
25
  end
26
-
26
+
27
27
  def ip_to_name(m, params)
28
28
  begin
29
29
  a = gethostname(params[:ip])
@@ -10,7 +10,7 @@
10
10
  # Automatically auths with Q on QuakeNet servers
11
11
 
12
12
  class QPlugin < Plugin
13
-
13
+
14
14
  def help(plugin, topic="")
15
15
  case topic
16
16
  when ""
@@ -23,7 +23,7 @@ class QPlugin < Plugin
23
23
  return "qauth register <email>: register with Q, an email on how to proceed will be sent to the email address you provide"
24
24
  end
25
25
  end
26
-
26
+
27
27
  def initialize
28
28
  super
29
29
  # this plugin only wants to store strings!
@@ -43,9 +43,9 @@ class QPlugin < Plugin
43
43
  @registry['quakenet.auth'] = params[:password]
44
44
  m.okay
45
45
  end
46
-
46
+
47
47
  def connect
48
- identify(nil, nil) if on_quakenet?
48
+ identify(nil, {}) if on_quakenet?
49
49
  end
50
50
 
51
51
  def identify(m, params)
@@ -3,17 +3,20 @@
3
3
  #
4
4
  # :title: Quotes plugin
5
5
  #
6
- # TODO:: use message mapper instead of multiple ifs
7
6
  # TODO:: switch to db
8
7
 
9
8
  define_structure :Quote, :num, :date, :source, :quote
10
9
 
11
10
  class QuotePlugin < Plugin
11
+ def dirname
12
+ 'quotes'
13
+ end
14
+
12
15
  def initialize
13
16
  super
14
17
  @lists = Hash.new
15
18
  @changed = Hash.new
16
- Dir["#{@bot.botclass}/quotes/*"].each {|f|
19
+ Dir[datafile('*')].each {|f|
17
20
  next if File.directory?(f)
18
21
  channel = File.basename(f)
19
22
  @lists[channel] = Array.new if(!@lists.has_key?(channel))
@@ -28,13 +31,13 @@ class QuotePlugin < Plugin
28
31
  end
29
32
 
30
33
  def save
31
- Dir.mkdir("#{@bot.botclass}/quotes") if(!FileTest.directory?("#{@bot.botclass}/quotes"))
34
+ Dir.mkdir(datafile) unless FileTest.directory? datafile
32
35
  @lists.each {|channel, quotes|
33
36
  begin
34
37
  if @changed[channel]
35
38
  debug "Writing new quotefile for channel #{channel} ..."
36
- Utils.safe_save("#{@bot.botclass}/quotes/#{channel}") {|file|
37
- quotes.compact.each {|q|
39
+ Utils.safe_save(datafile(channel)) {|file|
40
+ quotes.compact.each {|q|
38
41
  file.puts "#{q.num} | #{q.date} | #{q.source} | #{q.quote}"
39
42
  }
40
43
  }
@@ -62,7 +65,7 @@ class QuotePlugin < Plugin
62
65
 
63
66
  def addquote(source, channel, quote)
64
67
  @lists[channel] = Array.new if(!@lists.has_key?(channel))
65
- num = @lists[channel].length
68
+ num = @lists[channel].length
66
69
  @lists[channel][num] = Quote.new(num, Time.new, source.fullform, quote)
67
70
  @changed[channel] = true
68
71
  return num
@@ -77,7 +80,7 @@ class QuotePlugin < Plugin
77
80
  end
78
81
  else
79
82
  # random quote
80
- return @lists[channel].compact[rand(@lists[channel].nitems)],
83
+ return @lists[channel].compact.pick_one,
81
84
  @lists[channel].length - 1
82
85
  end
83
86
  end
@@ -123,6 +126,17 @@ class QuotePlugin < Plugin
123
126
  end
124
127
  end
125
128
 
129
+ def listquotes(source, channel, regexp)
130
+ return nil unless(@lists.has_key?(channel))
131
+ return nil unless(@lists[channel].length > 0)
132
+ matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
133
+ if matches.length > 0
134
+ return matches
135
+ else
136
+ return nil
137
+ end
138
+ end
139
+
126
140
  def help(plugin, topic="")
127
141
  case plugin
128
142
  when "addquote"
@@ -133,6 +147,8 @@ class QuotePlugin < Plugin
133
147
  _("getquote [<channel>] [<num>] => get quote from <channel> with number <num>. You only need to supply <channel> if you are addressing %{nick} privately. Without <num>, a random quote will be returned.") % { :nick => @bot.nick }
134
148
  when "searchquote"
135
149
  _("searchquote [<channel>] <regexp> => search for quote from <channel> that matches <regexp>. You only need to supply <channel> if you are addressing %{nick} privately.") % { :nick => @bot.nick }
150
+ when "listquotes"
151
+ _("listquotes [<channel>] <regexp> => list the quotes from <channel> that match <regexp>. You only need to supply <channel> if you are addressing %{nick} privately.") % { :nick => @bot.nick }
136
152
  when "topicquote"
137
153
  _("topicquote [<channel>] [<num>] => set topic to quote from <channel> with number <num>. You only need to supply <channel> if you are addressing %{nick} privately. Without <num>, a random quote will be set.") % { :nick => @bot.nick }
138
154
  when "countquote"
@@ -144,7 +160,7 @@ class QuotePlugin < Plugin
144
160
  when "lastquote"
145
161
  _("lastquote [<channel>] => show the last quote in a given channel. You only need to supply <channel> if you are addressing %{nick} privately") % { :nick => @bot.nick }
146
162
  else
147
- _("Quote module (Quote storage and retrieval) topics: addquote, delquote, getquote, searchquote, topicquote, countquote, whoquote, whenquote, lastquote") % { :nick => @bot.nick }
163
+ _("Quote module (Quote storage and retrieval) topics: addquote, delquote, getquote, searchquote, listquotes, topicquote, countquote, whoquote, whenquote, lastquote") % { :nick => @bot.nick }
148
164
  end
149
165
  end
150
166
 
@@ -221,6 +237,20 @@ class QuotePlugin < Plugin
221
237
  end
222
238
  end
223
239
 
240
+ def cmd_listquotes(m, p)
241
+ channel = p[:channel] || m.channel.to_s
242
+ reg = p[:reg].to_s
243
+ if quotes = listquotes(m.source, channel, reg)
244
+ m.reply _("%{total} quotes matching %{reg} found: %{list}") % {
245
+ :total => quotes.size,
246
+ :reg => reg,
247
+ :list => quotes.map {|q| q.num }.join(', ')
248
+ }
249
+ else
250
+ m.reply _("quote not found!")
251
+ end
252
+ end
253
+
224
254
  def cmd_countquote(m, p)
225
255
  channel = p[:channel] || m.channel.to_s
226
256
  reg = p[:reg] ? p[:reg].to_s : nil
@@ -266,6 +296,20 @@ end
266
296
  plugin = QuotePlugin.new
267
297
  plugin.register("quotes")
268
298
 
299
+ plugin.default_auth('other::edit', false) # Prevent random people from editing other channels quote lists by default
300
+ plugin.default_auth('other::view', true) # But allow them to view them
301
+
302
+ plugin.map "addquote :channel *quote", :action => :cmd_addquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::edit::add!'
303
+ plugin.map "delquote :channel :num", :action => :cmd_delquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::edit::del!'
304
+ plugin.map "getquote :channel [:num]", :action => :cmd_getquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::get!'
305
+ plugin.map "whoquote :channel :num", :action => :cmd_whoquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::who!'
306
+ plugin.map "whenquote :channel :num", :action => :cmd_whenquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::when!'
307
+ plugin.map "searchquote :channel *reg", :action => :cmd_searchquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::search!'
308
+ plugin.map "listquotes :channel *reg", :action => :cmd_listquotes, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::list!'
309
+ plugin.map "countquote :channel [*reg]", :action => :cmd_countquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::count!'
310
+ plugin.map "topicquote :channel [:num]", :action => :cmd_topicquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::topic!'
311
+ plugin.map "lastquote :channel", :action => :cmd_lastquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::last!'
312
+
269
313
  plugin.default_auth('edit', false) # Prevent random people from removing quotes
270
314
  plugin.default_auth('edit::add', true) # But allow them to add them
271
315
 
@@ -275,18 +319,8 @@ plugin.map "getquote [:num]", :action => :cmd_getquote, :private => false, :requ
275
319
  plugin.map "whoquote :num", :action => :cmd_whoquote, :private => false, :requirements => { :num => /^\d+$/ }, :auth_path => '!quote::view::who!'
276
320
  plugin.map "whenquote :num", :action => :cmd_whenquote, :private => false, :requirements => { :num => /^\d+$/ }, :auth_path => '!quote::view::when!'
277
321
  plugin.map "searchquote *reg", :action => :cmd_searchquote, :private => false, :auth_path => '!quote::view::search!'
322
+ plugin.map "listquotes *reg", :action => :cmd_listquotes, :private => false, :auth_path => '!quote::view::list!'
278
323
  plugin.map "countquote [*reg]", :action => :cmd_countquote, :private => false, :auth_path => '!quote::view::count!'
279
324
  plugin.map "topicquote [:num]", :action => :cmd_topicquote, :private => false, :requirements => { :num => /^\d+$/ }, :auth_path => '!quote::topic!'
280
325
  plugin.map "lastquote", :action => :cmd_lastquote, :private => false, :auth_path => '!quote::view::last!'
281
326
 
282
- plugin.default_auth('other::edit', false) # Prevent random people from editing other channels quote lists by default
283
- plugin.default_auth('other::view', true) # But allow them to view them
284
-
285
- plugin.map "addquote :channel *quote", :action => :cmd_addquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::edit::add!'
286
- plugin.map "delquote :channel :num", :action => :cmd_delquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::edit::del!'
287
- plugin.map "getquote :channel [:num]", :action => :cmd_getquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::get!'
288
- plugin.map "whoquote :channel :num", :action => :cmd_whoquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::who!'
289
- plugin.map "whenquote :channel :num", :action => :cmd_whenquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::view::when!'
290
- plugin.map "searchquote :channel *reg", :action => :cmd_searchquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::search!'
291
- plugin.map "countquote [:channel] [*reg]", :action => :cmd_countquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN }, :auth_path => '!quote::other::view::count!'
292
- plugin.map "topicquote :channel [:num]", :action => :cmd_topicquote, :requirements => { :channel => Regexp::Irc::GEN_CHAN, :num => /^\d+$/ }, :auth_path => '!quote::other::topic!'
@@ -69,7 +69,9 @@ class ::Reaction
69
69
  @trigger << Regexp.new(rex, true)
70
70
  else
71
71
  rex.sub!(/^(["'])(.*)\1$/, '\2')
72
- @trigger << Regexp.new(/\b#{Regexp.escape(rex)}(?:\b|$)/ui)
72
+ prepend = ( rex =~ /^\w/ ? '(?:\b)' : '')
73
+ append = ( rex =~ /\w$/ ? '(?:\b|$)' : '')
74
+ @trigger << Regexp.new(/#{prepend}#{Regexp.escape(rex)}#{append}/ui)
73
75
  end
74
76
  end
75
77
 
@@ -81,6 +83,8 @@ class ::Reaction
81
83
  act = :act
82
84
  elsif rex.sub!(/^(?:cmd|command):/,'')
83
85
  act = :cmd
86
+ elsif rex.sub!(/^ruby:/,'')
87
+ act = :ruby
84
88
  end
85
89
  @replies << Reply.new(self, act, rex, *args)
86
90
  make_ranges
@@ -170,7 +174,7 @@ class ReactionPlugin < Plugin
170
174
  super
171
175
  if @registry.has_key?(:reactions)
172
176
  @reactions = @registry[:reactions]
173
- raise unless @reactions
177
+ raise LoadError, "corrupted reaction database" unless @reactions
174
178
  else
175
179
  @reactions = []
176
180
  end
@@ -202,28 +206,33 @@ class ReactionPlugin < Plugin
202
206
  help(:react)
203
207
  when :remove, :delete, :rm, :del
204
208
  "reaction #{topic} <trigger> [<n>] => removes reactions to expression <trigger>. If <n> (a positive integer) is specified, only remove the n-th reaction, otherwise remove the trigger completely"
209
+ when :move
210
+ "reaction move <trigger> to <other> => move all reactions to <trigger> to the new trigger <other>"
205
211
  when :chance, :chances
206
212
  "reaction chances are expressed either in terms of percentage (like 30%) or in terms of floating point numbers (like 0.3), and are clipped to be " +
207
213
  "between 0 and 1 (i.e. 0% and 100%). A reaction can have multiple replies, each with a different chance; if the total of the chances is less than one, " +
208
214
  "there is a chance that the trigger will not actually cause a reply. Otherwise, the chances express the relative frequency of the replies."
209
215
  when :trigger, :triggers
210
- "reaction triggers can have one of the format: single_word 'multiple words' \"multiple words \" /regular_expression/ !regular_expression!. " +
216
+ "reaction triggers can have one of the format: single_word 'multiple words' \"multiple words \" /regular_expression/ !regular_expression!. " +
211
217
  "If prefixed by 'act:' (e.g. act:/(order|command)s/) the bot will only respond if a CTCP ACTION matches the trigger"
212
218
  when :reply, :replies
213
219
  "reaction replies are simply messages that the bot will reply when a trigger is matched. " +
214
- "Replies can be prefixed by 'act:' (e.g. act:goes shopping) to signify that the bot should act instead of saying the message. " +
215
- "Replies can be prefixed by 'cmd:' or 'command:' (e.g. cmd:lart %{who}) to issue a command to the bot. " +
216
- "Replies can use the %{key} syntax to access one of the following keys: " +
217
- "who (the user that said the trigger), bot (the bot's own nick), " +
218
- "target (the first word following the trigger), what (whatever follows target), " +
220
+ "Replies prefixed by 'act:' (e.g. act:goes shopping) signify that the bot should act instead of saying the message. " +
221
+ "Replies prefixed by 'cmd:' or 'command:' (e.g. cmd:lart %{who}) issue a command to the bot. " +
222
+ "Replies can use the %{key} syntax to access the following keys: " +
223
+ "who (user that said the trigger), bot (bot's own nick), " +
224
+ "target (first word following the trigger), what (whatever follows target), " +
219
225
  "before (everything that precedes the trigger), after, (everything that follows the trigger), " +
220
- "match (the actual matched text), match1, match2, ... (the i-th capture)"
226
+ "match (matched text), match1, match2, ... (the i-th capture). " +
227
+ "Replies prefixed by 'ruby:' (e.g. ruby:m.reply 'Hello ' + subs[:who]) are interpreted as ruby code. " +
228
+ "No %{key} substitution is done in this case, use the subs hash in the code instead. " +
229
+ "Be warned that creating ruby replies can open unexpected security holes in the bot."
221
230
  when :list
222
231
  "reaction list [n]: lists the n-the page of programmed reactions (30 reactions are listed per page)"
223
232
  when :show
224
233
  "reaction show <trigger>: list the programmed replies to trigger <trigger>"
225
234
  else
226
- "reaction topics: add, remove, delete, rm, del, triggers, replies, chance, list, show"
235
+ "reaction topics: add, remove, delete, rm, del, move, triggers, replies, chance, list, show"
227
236
  end
228
237
  end
229
238
 
@@ -261,18 +270,28 @@ class ReactionPlugin < Plugin
261
270
  reply = wanted.pick_reply
262
271
  debug "picked #{reply}"
263
272
  return unless reply
264
- args = reply.apply(subs)
265
- if args[0] == :cmd
273
+ act, arg = reply.apply(subs)
274
+ case act
275
+ when :ruby
276
+ begin
277
+ # no substitutions for ruby code
278
+ eval(reply.reply)
279
+ rescue Exception => e
280
+ error e
281
+ end
282
+ when :cmd
266
283
  begin
267
284
  # Pass the new message back to the bot.
268
285
  # FIXME Maybe we should do it the alias way, only calling
269
286
  # @bot.plugins.privmsg() ?
270
- fake_message(@bot.nick+": "+args[1], :from => m)
287
+ fake_message(@bot.nick+": "+arg, :from => m)
271
288
  rescue RecurseTooDeep => e
272
289
  error e
273
290
  end
291
+ when :reply
292
+ m.plainreply arg
274
293
  else
275
- m.__send__(*args)
294
+ m.__send__(act, arg)
276
295
  end
277
296
  end
278
297
 
@@ -282,6 +301,14 @@ class ReactionPlugin < Plugin
282
301
  }
283
302
  end
284
303
 
304
+ def can_add?(m, reaction)
305
+ return true if reaction.act == :reply
306
+ return true if reaction.act == :act
307
+ return true if reaction.act == :ruby and @bot.auth.permit?(m.source, "reaction::react::ruby", m.channel)
308
+ return true if reaction.act == :cmd and @bot.auth.permit?(m.source, "reaction::react::cmd", m.channel)
309
+ return false
310
+ end
311
+
285
312
  def handle_add(m, params)
286
313
  trigger = params[:trigger].to_s
287
314
  reply = params[:reply].to_s
@@ -293,20 +320,45 @@ class ReactionPlugin < Plugin
293
320
  pct = pct.to_f.clip(0,1)
294
321
  end
295
322
 
323
+ new_reaction = false
324
+
296
325
  reaction = find_reaction(trigger)
297
326
  if not reaction
298
327
  reaction = Reaction.new(trigger)
299
328
  @reactions << reaction
300
- m.reply "Ok, I'll start reacting to #{reaction.raw_trigger}"
329
+ new_reaction = true
301
330
  end
331
+
302
332
  found = reaction.find_reply(reply)
303
333
  if found
304
- found.pct = pct
305
- found.author = m.sourcenick
306
- found.date = Time.now
307
- found.channel = m.channel
334
+ # ruby replies need special permission
335
+ if can_add?(m, found)
336
+ found.pct = pct
337
+ found.author = m.sourcenick
338
+ found.date = Time.now
339
+ found.channel = m.channel
340
+ else
341
+ m.reply _("Sorry, you're not allowed to change %{act} replies here") % {
342
+ :act => found.act
343
+ }
344
+ return
345
+ end
308
346
  else
309
347
  found = reaction.add_reply(reply, pct, m.sourcenick, Time.now, m.channel)
348
+ unless can_add?(m, found)
349
+ m.reply _("Sorry, you're not allowed to add %{act} replies here") % {
350
+ :act => found.act
351
+ }
352
+ reaction.rm_reply(reaction.replies.length)
353
+ if new_reaction
354
+ @reactions.delete(reaction)
355
+ end
356
+ return
357
+ end
358
+ end
359
+
360
+ if new_reaction
361
+ m.reply "Ok, I'll start reacting to #{reaction.raw_trigger}"
310
362
  end
311
363
  m.reply "I'll react to #{reaction.raw_trigger} with #{reaction.raw_replies.last} (#{(reaction.replies.last.pct * 100).to_i}%)"
312
364
  end
@@ -396,6 +448,11 @@ plugin = ReactionPlugin.new
396
448
  plugin.map plugin.add_syntax, :action => 'handle_add',
397
449
  :requirements => { :trigger => plugin.trigger_syntax }
398
450
 
451
+ # ruby reactions are security holes, so give stricter permission
452
+ plugin.default_auth('react::ruby', false)
453
+ # cmd reactions can be security holes too
454
+ plugin.default_auth('react::cmd', false)
455
+
399
456
  plugin.map 'reaction list [:page]', :action => 'handle_list',
400
457
  :requirements => { :page => /^\d+$/ }
401
458