rbot 0.9.10 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (706) hide show
  1. data/AUTHORS +22 -4
  2. data/COPYING +5 -18
  3. data/ChangeLog +396 -0
  4. data/README +23 -25
  5. data/REQUIREMENTS +58 -12
  6. data/Rakefile +215 -0
  7. data/bin/rbot +47 -25
  8. data/bin/rbot-remote +76 -0
  9. data/data/rbot/languages/french.lang +5 -5
  10. data/data/rbot/languages/german.lang +4 -4
  11. data/data/rbot/languages/italian.lang +53 -0
  12. data/data/rbot/languages/japanese.lang +43 -0
  13. data/data/rbot/languages/russian.lang +60 -60
  14. data/data/rbot/languages/traditional_chinese.lang +75 -0
  15. data/data/rbot/plugins/alias.rb +192 -0
  16. data/data/rbot/plugins/autoop.rb +74 -37
  17. data/data/rbot/plugins/autorejoin.rb +15 -5
  18. data/data/rbot/plugins/bans.rb +454 -150
  19. data/data/rbot/plugins/bash.rb +173 -31
  20. data/data/rbot/plugins/botsnack.rb +37 -0
  21. data/data/rbot/plugins/cal.rb +11 -2
  22. data/data/rbot/plugins/chanserv.rb +132 -0
  23. data/data/rbot/plugins/chucknorris.rb +2 -2
  24. data/data/rbot/plugins/chucknorris.yml.gz +0 -0
  25. data/data/rbot/plugins/debugger.rb +136 -0
  26. data/data/rbot/plugins/deepthoughts.rb +89 -95
  27. data/data/rbot/plugins/delicious.rb +109 -0
  28. data/data/rbot/plugins/dice.rb +77 -25
  29. data/data/rbot/plugins/dict.rb +243 -0
  30. data/data/rbot/plugins/dictclient.rb +211 -0
  31. data/data/rbot/plugins/digg.rb +2 -2
  32. data/data/rbot/plugins/excuse.rb +4 -4
  33. data/data/rbot/plugins/factoids.rb +506 -0
  34. data/data/rbot/plugins/figlet.rb +127 -18
  35. data/data/rbot/plugins/fish.rb +93 -33
  36. data/data/rbot/plugins/forecast.rb +40 -27
  37. data/data/rbot/plugins/fortune.rb +86 -11
  38. data/data/rbot/plugins/freshmeat.rb +46 -12
  39. data/data/rbot/plugins/games/azgame.rb +565 -0
  40. data/data/rbot/plugins/games/quiz.rb +961 -0
  41. data/data/rbot/plugins/games/roshambo.rb +62 -0
  42. data/data/rbot/plugins/{roulette.rb → games/roulette.rb} +58 -8
  43. data/data/rbot/plugins/games/shiritori.rb +485 -0
  44. data/data/rbot/plugins/games/uno.rb +1205 -0
  45. data/data/rbot/plugins/games/wheelfortune.rb +615 -0
  46. data/data/rbot/plugins/grouphug.rb +76 -22
  47. data/data/rbot/plugins/hl2.rb +97 -0
  48. data/data/rbot/plugins/host.rb +10 -1
  49. data/data/rbot/plugins/httpd.rb.disabled +1 -0
  50. data/data/rbot/plugins/imdb.rb +511 -47
  51. data/data/rbot/plugins/insult.rb +32 -37
  52. data/data/rbot/plugins/iplookup.rb +224 -224
  53. data/data/rbot/plugins/karma.rb +45 -21
  54. data/data/rbot/plugins/keywords.rb +258 -160
  55. data/data/rbot/plugins/lart.rb +126 -95
  56. data/data/rbot/plugins/lastfm.rb +421 -12
  57. data/data/rbot/plugins/linkbot.rb +75 -0
  58. data/data/rbot/plugins/markov.rb +103 -46
  59. data/data/rbot/plugins/math.rb +6 -5
  60. data/data/rbot/plugins/modes.rb +114 -0
  61. data/data/rbot/plugins/nickrecover.rb +97 -0
  62. data/data/rbot/plugins/nickserv.rb +111 -48
  63. data/data/rbot/plugins/nslookup.rb +15 -19
  64. data/data/rbot/plugins/plugin.header +11 -0
  65. data/data/rbot/plugins/quakeauth.rb +74 -11
  66. data/data/rbot/plugins/quotes.rb +189 -233
  67. data/data/rbot/plugins/reaction.rb +415 -0
  68. data/data/rbot/plugins/remind.rb +11 -15
  69. data/data/rbot/plugins/remotectl.rb +29 -0
  70. data/data/rbot/plugins/ri.rb +74 -0
  71. data/data/rbot/plugins/rot13.rb +16 -1
  72. data/data/rbot/plugins/rss.rb +1219 -585
  73. data/data/rbot/plugins/salut.rb +226 -0
  74. data/data/rbot/plugins/script.rb +184 -0
  75. data/data/rbot/plugins/search.rb +233 -0
  76. data/data/rbot/plugins/seen.rb +44 -40
  77. data/data/rbot/plugins/shortenurls.rb +93 -0
  78. data/data/rbot/plugins/slashdot.rb +49 -11
  79. data/data/rbot/plugins/spell.rb +37 -27
  80. data/data/rbot/plugins/theyfightcrime.rb +36 -39
  81. data/data/rbot/plugins/threat.rb +25 -22
  82. data/data/rbot/plugins/time.rb +135 -0
  83. data/data/rbot/plugins/topic.rb +107 -50
  84. data/data/rbot/plugins/translator.rb +360 -0
  85. data/data/rbot/plugins/tube.rb +28 -40
  86. data/data/rbot/plugins/twitter.rb +187 -0
  87. data/data/rbot/plugins/urban.rb +62 -53
  88. data/data/rbot/plugins/url.rb +193 -409
  89. data/data/rbot/plugins/usermodes.rb +26 -0
  90. data/data/rbot/plugins/wall.rb +60 -0
  91. data/data/rbot/plugins/weather.rb +254 -598
  92. data/data/rbot/plugins/wow.rb +41 -37
  93. data/data/rbot/plugins/wserver.rb +17 -25
  94. data/data/rbot/plugins/youtube.rb +237 -0
  95. data/data/rbot/templates/lart/{larts → larts-english} +0 -0
  96. data/data/rbot/templates/lart/larts-italian +25 -0
  97. data/data/rbot/templates/lart/larts-japanese +10 -0
  98. data/data/rbot/templates/lart/{praises → praises-english} +0 -0
  99. data/data/rbot/templates/lart/praises-italian +4 -0
  100. data/data/rbot/templates/lart/praises-japanese +3 -0
  101. data/data/rbot/templates/quiz/win_messages +11 -0
  102. data/data/rbot/templates/salut/salut-english +40 -0
  103. data/data/rbot/templates/salut/salut-french +29 -0
  104. data/data/rbot/templates/salut/salut-italian +51 -0
  105. data/data/rbot/templates/salut/salut-japanese +31 -0
  106. data/lib/rbot/botuser.rb +940 -0
  107. data/lib/rbot/config-compat.rb +23 -0
  108. data/lib/rbot/config.rb +211 -276
  109. data/lib/rbot/core/auth.rb +1063 -0
  110. data/lib/rbot/core/basics.rb +214 -0
  111. data/lib/rbot/core/config.rb +330 -0
  112. data/lib/rbot/core/filters_ui.rb +64 -0
  113. data/lib/rbot/core/irclog.rb +292 -0
  114. data/lib/rbot/core/remote.rb +401 -0
  115. data/lib/rbot/core/unicode.rb +91 -0
  116. data/lib/rbot/core/userdata.rb +200 -0
  117. data/lib/rbot/core/utils/extends.rb +444 -0
  118. data/lib/rbot/core/utils/filters.rb +154 -0
  119. data/lib/rbot/core/utils/httputil.rb +689 -0
  120. data/lib/rbot/core/utils/utils.rb +717 -0
  121. data/lib/rbot/dbhash.rb +30 -9
  122. data/lib/rbot/irc.rb +1966 -0
  123. data/lib/rbot/ircbot.rb +816 -675
  124. data/lib/rbot/ircsocket.rb +246 -232
  125. data/lib/rbot/language.rb +86 -8
  126. data/lib/rbot/load-gettext.rb +154 -0
  127. data/lib/rbot/maskdb.rb +162 -0
  128. data/lib/rbot/message.rb +392 -65
  129. data/lib/rbot/messagemapper.rb +438 -94
  130. data/lib/rbot/pkgconfig.rb +6 -0
  131. data/lib/rbot/plugins.rb +713 -172
  132. data/lib/rbot/post-config.rb +1 -0
  133. data/lib/rbot/rbotconfig.rb +31 -12
  134. data/lib/rbot/registry.rb +79 -48
  135. data/lib/rbot/rfc2812.rb +927 -519
  136. data/lib/rbot/timer.rb +232 -167
  137. data/po/en_US/rbot-alias.po +82 -0
  138. data/po/en_US/rbot-autoop.po +0 -0
  139. data/po/en_US/rbot-autorejoin.po +0 -0
  140. data/po/en_US/rbot-azgame.po +200 -0
  141. data/po/en_US/rbot-bans.po +0 -0
  142. data/po/en_US/rbot-bash.po +0 -0
  143. data/po/en_US/rbot-botsnack.po +0 -0
  144. data/po/en_US/rbot-cal.po +3 -0
  145. data/po/en_US/rbot-chanserv.po +0 -0
  146. data/po/en_US/rbot-chucknorris.po +0 -0
  147. data/po/en_US/rbot-debugger.po +0 -0
  148. data/po/en_US/rbot-deepthoughts.po +0 -0
  149. data/po/en_US/rbot-delicious.po +0 -0
  150. data/po/en_US/rbot-dice.po +0 -0
  151. data/po/en_US/rbot-dict.po +0 -0
  152. data/po/en_US/rbot-dictclient.po +124 -0
  153. data/po/en_US/rbot-digg.po +0 -0
  154. data/po/en_US/rbot-eightball.po +0 -0
  155. data/po/en_US/rbot-excuse.po +0 -0
  156. data/po/en_US/rbot-factoids.po +107 -0
  157. data/po/en_US/rbot-figlet.po +36 -0
  158. data/po/en_US/rbot-fish.po +0 -0
  159. data/po/en_US/rbot-forecast.po +0 -0
  160. data/po/en_US/rbot-fortune.po +0 -0
  161. data/po/en_US/rbot-freshmeat.po +0 -0
  162. data/po/en_US/rbot-grouphug.po +18 -0
  163. data/po/en_US/rbot-hl2.po +0 -0
  164. data/po/en_US/rbot-host.po +3 -0
  165. data/po/en_US/rbot-imdb.po +0 -0
  166. data/po/en_US/rbot-insult.po +0 -0
  167. data/po/en_US/rbot-iplookup.po +0 -0
  168. data/po/en_US/rbot-karma.po +0 -0
  169. data/po/en_US/rbot-keywords.po +24 -0
  170. data/po/en_US/rbot-lart.po +0 -0
  171. data/po/en_US/rbot-lastfm.po +172 -0
  172. data/po/en_US/rbot-linkbot.po +0 -0
  173. data/po/en_US/rbot-markov.po +20 -0
  174. data/po/en_US/rbot-math.po +0 -0
  175. data/po/en_US/rbot-modes.po +0 -0
  176. data/po/en_US/rbot-nickrecover.po +40 -0
  177. data/po/en_US/rbot-nickserv.po +104 -0
  178. data/po/en_US/rbot-nslookup.po +0 -0
  179. data/po/en_US/rbot-quakeauth.po +0 -0
  180. data/po/en_US/rbot-quiz.po +0 -0
  181. data/po/en_US/rbot-quotes.po +108 -0
  182. data/po/en_US/rbot-reaction.po +0 -0
  183. data/po/en_US/rbot-remind.po +0 -0
  184. data/po/en_US/rbot-remotectl.po +0 -0
  185. data/po/en_US/rbot-ri.po +0 -0
  186. data/po/en_US/rbot-roshambo.po +0 -0
  187. data/po/en_US/rbot-rot13.po +0 -0
  188. data/po/en_US/rbot-roulette.po +0 -0
  189. data/po/en_US/rbot-rss.po +20 -0
  190. data/po/en_US/rbot-salut.po +0 -0
  191. data/po/en_US/rbot-script.po +0 -0
  192. data/po/en_US/rbot-search.po +0 -0
  193. data/po/en_US/rbot-seen.po +0 -0
  194. data/po/en_US/rbot-shiritori.po +113 -0
  195. data/po/en_US/rbot-shortenurls.po +0 -0
  196. data/po/en_US/rbot-slashdot.po +0 -0
  197. data/po/en_US/rbot-spell.po +54 -0
  198. data/po/en_US/rbot-theyfightcrime.po +0 -0
  199. data/po/en_US/rbot-threat.po +0 -0
  200. data/po/en_US/rbot-time.po +0 -0
  201. data/po/en_US/rbot-topic.po +0 -0
  202. data/po/en_US/rbot-translator.po +77 -0
  203. data/po/en_US/rbot-tube.po +0 -0
  204. data/po/en_US/rbot-twitter.po +24 -0
  205. data/po/en_US/rbot-uno.po +512 -0
  206. data/po/en_US/rbot-urban.po +0 -0
  207. data/po/en_US/rbot-url.po +0 -0
  208. data/po/en_US/rbot-usermodes.po +0 -0
  209. data/po/en_US/rbot-wall.po +33 -0
  210. data/po/en_US/rbot-weather.po +0 -0
  211. data/po/en_US/rbot-wheelfortune.po +205 -0
  212. data/po/en_US/rbot-wow.po +0 -0
  213. data/po/en_US/rbot-wserver.po +0 -0
  214. data/po/en_US/rbot-youtube.po +58 -0
  215. data/po/en_US/rbot.po +1097 -0
  216. data/po/fr/rbot-alias.po +97 -0
  217. data/po/fr/rbot-autoop.po +0 -0
  218. data/po/fr/rbot-autorejoin.po +0 -0
  219. data/po/fr/rbot-azgame.po +204 -0
  220. data/po/fr/rbot-bans.po +0 -0
  221. data/po/fr/rbot-bash.po +0 -0
  222. data/po/fr/rbot-botsnack.po +0 -0
  223. data/po/fr/rbot-cal.po +3 -0
  224. data/po/fr/rbot-chanserv.po +0 -0
  225. data/po/fr/rbot-chucknorris.po +0 -0
  226. data/po/fr/rbot-debugger.po +0 -0
  227. data/po/fr/rbot-deepthoughts.po +0 -0
  228. data/po/fr/rbot-delicious.po +0 -0
  229. data/po/fr/rbot-dice.po +0 -0
  230. data/po/fr/rbot-dict.po +0 -0
  231. data/po/fr/rbot-dictclient.po +134 -0
  232. data/po/fr/rbot-digg.po +0 -0
  233. data/po/fr/rbot-eightball.po +0 -0
  234. data/po/fr/rbot-excuse.po +0 -0
  235. data/po/fr/rbot-factoids.po +111 -0
  236. data/po/fr/rbot-figlet.po +37 -0
  237. data/po/fr/rbot-fish.po +0 -0
  238. data/po/fr/rbot-forecast.po +0 -0
  239. data/po/fr/rbot-fortune.po +0 -0
  240. data/po/fr/rbot-freshmeat.po +0 -0
  241. data/po/fr/rbot-grouphug.po +18 -0
  242. data/po/fr/rbot-hl2.po +0 -0
  243. data/po/fr/rbot-host.po +3 -0
  244. data/po/fr/rbot-imdb.po +0 -0
  245. data/po/fr/rbot-insult.po +0 -0
  246. data/po/fr/rbot-iplookup.po +0 -0
  247. data/po/fr/rbot-karma.po +0 -0
  248. data/po/fr/rbot-keywords.po +24 -0
  249. data/po/fr/rbot-lart.po +0 -0
  250. data/po/fr/rbot-lastfm.po +172 -0
  251. data/po/fr/rbot-linkbot.po +0 -0
  252. data/po/fr/rbot-markov.po +20 -0
  253. data/po/fr/rbot-math.po +0 -0
  254. data/po/fr/rbot-modes.po +0 -0
  255. data/po/fr/rbot-nickrecover.po +36 -0
  256. data/po/fr/rbot-nickserv.po +116 -0
  257. data/po/fr/rbot-nslookup.po +0 -0
  258. data/po/fr/rbot-quakeauth.po +0 -0
  259. data/po/fr/rbot-quiz.po +0 -0
  260. data/po/fr/rbot-quotes.po +138 -0
  261. data/po/fr/rbot-reaction.po +0 -0
  262. data/po/fr/rbot-remind.po +0 -0
  263. data/po/fr/rbot-remotectl.po +0 -0
  264. data/po/fr/rbot-ri.po +0 -0
  265. data/po/fr/rbot-roshambo.po +0 -0
  266. data/po/fr/rbot-rot13.po +0 -0
  267. data/po/fr/rbot-roulette.po +0 -0
  268. data/po/fr/rbot-rss.po +20 -0
  269. data/po/fr/rbot-salut.po +0 -0
  270. data/po/fr/rbot-script.po +0 -0
  271. data/po/fr/rbot-search.po +0 -0
  272. data/po/fr/rbot-seen.po +0 -0
  273. data/po/fr/rbot-shiritori.po +119 -0
  274. data/po/fr/rbot-shortenurls.po +0 -0
  275. data/po/fr/rbot-slashdot.po +0 -0
  276. data/po/fr/rbot-spell.po +56 -0
  277. data/po/fr/rbot-theyfightcrime.po +0 -0
  278. data/po/fr/rbot-threat.po +0 -0
  279. data/po/fr/rbot-time.po +0 -0
  280. data/po/fr/rbot-topic.po +0 -0
  281. data/po/fr/rbot-translator.po +90 -0
  282. data/po/fr/rbot-tube.po +0 -0
  283. data/po/fr/rbot-twitter.po +24 -0
  284. data/po/fr/rbot-uno.po +521 -0
  285. data/po/fr/rbot-urban.po +0 -0
  286. data/po/fr/rbot-url.po +0 -0
  287. data/po/fr/rbot-usermodes.po +0 -0
  288. data/po/fr/rbot-wall.po +33 -0
  289. data/po/fr/rbot-weather.po +0 -0
  290. data/po/fr/rbot-wheelfortune.po +255 -0
  291. data/po/fr/rbot-wow.po +0 -0
  292. data/po/fr/rbot-wserver.po +0 -0
  293. data/po/fr/rbot-youtube.po +64 -0
  294. data/po/fr/rbot.po +1133 -0
  295. data/po/it/rbot-alias.po +82 -0
  296. data/po/it/rbot-autoop.po +0 -0
  297. data/po/it/rbot-autorejoin.po +0 -0
  298. data/po/it/rbot-azgame.po +186 -0
  299. data/po/it/rbot-bans.po +0 -0
  300. data/po/it/rbot-bash.po +0 -0
  301. data/po/it/rbot-botsnack.po +0 -0
  302. data/po/it/rbot-cal.po +3 -0
  303. data/po/it/rbot-chanserv.po +0 -0
  304. data/po/it/rbot-chucknorris.po +0 -0
  305. data/po/it/rbot-debugger.po +0 -0
  306. data/po/it/rbot-deepthoughts.po +0 -0
  307. data/po/it/rbot-delicious.po +0 -0
  308. data/po/it/rbot-dice.po +0 -0
  309. data/po/it/rbot-dict.po +0 -0
  310. data/po/it/rbot-dictclient.po +111 -0
  311. data/po/it/rbot-digg.po +0 -0
  312. data/po/it/rbot-eightball.po +0 -0
  313. data/po/it/rbot-excuse.po +0 -0
  314. data/po/it/rbot-factoids.po +110 -0
  315. data/po/it/rbot-figlet.po +36 -0
  316. data/po/it/rbot-fish.po +0 -0
  317. data/po/it/rbot-forecast.po +0 -0
  318. data/po/it/rbot-fortune.po +0 -0
  319. data/po/it/rbot-freshmeat.po +0 -0
  320. data/po/it/rbot-grouphug.po +18 -0
  321. data/po/it/rbot-hl2.po +0 -0
  322. data/po/it/rbot-host.po +3 -0
  323. data/po/it/rbot-imdb.po +0 -0
  324. data/po/it/rbot-insult.po +0 -0
  325. data/po/it/rbot-iplookup.po +0 -0
  326. data/po/it/rbot-karma.po +0 -0
  327. data/po/it/rbot-keywords.po +24 -0
  328. data/po/it/rbot-lart.po +0 -0
  329. data/po/it/rbot-lastfm.po +172 -0
  330. data/po/it/rbot-linkbot.po +0 -0
  331. data/po/it/rbot-markov.po +20 -0
  332. data/po/it/rbot-math.po +0 -0
  333. data/po/it/rbot-modes.po +0 -0
  334. data/po/it/rbot-nickrecover.po +36 -0
  335. data/po/it/rbot-nickserv.po +104 -0
  336. data/po/it/rbot-nslookup.po +0 -0
  337. data/po/it/rbot-quakeauth.po +0 -0
  338. data/po/it/rbot-quiz.po +0 -0
  339. data/po/it/rbot-quotes.po +108 -0
  340. data/po/it/rbot-reaction.po +0 -0
  341. data/po/it/rbot-remind.po +0 -0
  342. data/po/it/rbot-remotectl.po +0 -0
  343. data/po/it/rbot-ri.po +0 -0
  344. data/po/it/rbot-roshambo.po +0 -0
  345. data/po/it/rbot-rot13.po +0 -0
  346. data/po/it/rbot-roulette.po +0 -0
  347. data/po/it/rbot-rss.po +20 -0
  348. data/po/it/rbot-salut.po +0 -0
  349. data/po/it/rbot-script.po +0 -0
  350. data/po/it/rbot-search.po +0 -0
  351. data/po/it/rbot-seen.po +0 -0
  352. data/po/it/rbot-shiritori.po +102 -0
  353. data/po/it/rbot-shortenurls.po +0 -0
  354. data/po/it/rbot-slashdot.po +0 -0
  355. data/po/it/rbot-spell.po +54 -0
  356. data/po/it/rbot-theyfightcrime.po +0 -0
  357. data/po/it/rbot-threat.po +0 -0
  358. data/po/it/rbot-time.po +0 -0
  359. data/po/it/rbot-topic.po +0 -0
  360. data/po/it/rbot-translator.po +77 -0
  361. data/po/it/rbot-tube.po +0 -0
  362. data/po/it/rbot-twitter.po +24 -0
  363. data/po/it/rbot-uno.po +564 -0
  364. data/po/it/rbot-urban.po +0 -0
  365. data/po/it/rbot-url.po +0 -0
  366. data/po/it/rbot-usermodes.po +0 -0
  367. data/po/it/rbot-wall.po +33 -0
  368. data/po/it/rbot-weather.po +0 -0
  369. data/po/it/rbot-wheelfortune.po +251 -0
  370. data/po/it/rbot-wow.po +0 -0
  371. data/po/it/rbot-wserver.po +0 -0
  372. data/po/it/rbot-youtube.po +64 -0
  373. data/po/it/rbot.po +1127 -0
  374. data/po/ja/rbot-alias.po +82 -0
  375. data/po/ja/rbot-autoop.po +0 -0
  376. data/po/ja/rbot-autorejoin.po +0 -0
  377. data/po/ja/rbot-azgame.po +194 -0
  378. data/po/ja/rbot-bans.po +0 -0
  379. data/po/ja/rbot-bash.po +0 -0
  380. data/po/ja/rbot-botsnack.po +0 -0
  381. data/po/ja/rbot-cal.po +3 -0
  382. data/po/ja/rbot-chanserv.po +0 -0
  383. data/po/ja/rbot-chucknorris.po +0 -0
  384. data/po/ja/rbot-debugger.po +0 -0
  385. data/po/ja/rbot-deepthoughts.po +0 -0
  386. data/po/ja/rbot-delicious.po +0 -0
  387. data/po/ja/rbot-dice.po +0 -0
  388. data/po/ja/rbot-dict.po +0 -0
  389. data/po/ja/rbot-dictclient.po +111 -0
  390. data/po/ja/rbot-digg.po +0 -0
  391. data/po/ja/rbot-eightball.po +0 -0
  392. data/po/ja/rbot-excuse.po +0 -0
  393. data/po/ja/rbot-factoids.po +107 -0
  394. data/po/ja/rbot-figlet.po +36 -0
  395. data/po/ja/rbot-fish.po +0 -0
  396. data/po/ja/rbot-forecast.po +0 -0
  397. data/po/ja/rbot-fortune.po +0 -0
  398. data/po/ja/rbot-freshmeat.po +0 -0
  399. data/po/ja/rbot-grouphug.po +18 -0
  400. data/po/ja/rbot-hl2.po +0 -0
  401. data/po/ja/rbot-host.po +3 -0
  402. data/po/ja/rbot-imdb.po +0 -0
  403. data/po/ja/rbot-insult.po +0 -0
  404. data/po/ja/rbot-iplookup.po +0 -0
  405. data/po/ja/rbot-karma.po +0 -0
  406. data/po/ja/rbot-keywords.po +24 -0
  407. data/po/ja/rbot-lart.po +0 -0
  408. data/po/ja/rbot-lastfm.po +172 -0
  409. data/po/ja/rbot-linkbot.po +0 -0
  410. data/po/ja/rbot-markov.po +20 -0
  411. data/po/ja/rbot-math.po +0 -0
  412. data/po/ja/rbot-modes.po +0 -0
  413. data/po/ja/rbot-nickrecover.po +36 -0
  414. data/po/ja/rbot-nickserv.po +104 -0
  415. data/po/ja/rbot-nslookup.po +0 -0
  416. data/po/ja/rbot-quakeauth.po +0 -0
  417. data/po/ja/rbot-quiz.po +0 -0
  418. data/po/ja/rbot-quotes.po +108 -0
  419. data/po/ja/rbot-reaction.po +0 -0
  420. data/po/ja/rbot-remind.po +0 -0
  421. data/po/ja/rbot-remotectl.po +0 -0
  422. data/po/ja/rbot-ri.po +0 -0
  423. data/po/ja/rbot-roshambo.po +0 -0
  424. data/po/ja/rbot-rot13.po +0 -0
  425. data/po/ja/rbot-roulette.po +0 -0
  426. data/po/ja/rbot-rss.po +20 -0
  427. data/po/ja/rbot-salut.po +0 -0
  428. data/po/ja/rbot-script.po +0 -0
  429. data/po/ja/rbot-search.po +0 -0
  430. data/po/ja/rbot-seen.po +0 -0
  431. data/po/ja/rbot-shiritori.po +115 -0
  432. data/po/ja/rbot-shortenurls.po +0 -0
  433. data/po/ja/rbot-slashdot.po +0 -0
  434. data/po/ja/rbot-spell.po +54 -0
  435. data/po/ja/rbot-theyfightcrime.po +0 -0
  436. data/po/ja/rbot-threat.po +0 -0
  437. data/po/ja/rbot-time.po +0 -0
  438. data/po/ja/rbot-topic.po +0 -0
  439. data/po/ja/rbot-translator.po +77 -0
  440. data/po/ja/rbot-tube.po +0 -0
  441. data/po/ja/rbot-twitter.po +24 -0
  442. data/po/ja/rbot-uno.po +512 -0
  443. data/po/ja/rbot-urban.po +0 -0
  444. data/po/ja/rbot-url.po +0 -0
  445. data/po/ja/rbot-usermodes.po +0 -0
  446. data/po/ja/rbot-wall.po +33 -0
  447. data/po/ja/rbot-weather.po +0 -0
  448. data/po/ja/rbot-wheelfortune.po +205 -0
  449. data/po/ja/rbot-wow.po +0 -0
  450. data/po/ja/rbot-wserver.po +0 -0
  451. data/po/ja/rbot-youtube.po +58 -0
  452. data/po/ja/rbot.po +999 -0
  453. data/po/rbot-alias.pot +83 -0
  454. data/po/rbot-autoop.pot +0 -0
  455. data/po/rbot-autorejoin.pot +0 -0
  456. data/po/rbot-azgame.pot +187 -0
  457. data/po/rbot-bans.pot +0 -0
  458. data/po/rbot-bash.pot +0 -0
  459. data/po/rbot-botsnack.pot +0 -0
  460. data/po/rbot-cal.pot +21 -0
  461. data/po/rbot-chanserv.pot +0 -0
  462. data/po/rbot-chucknorris.pot +0 -0
  463. data/po/rbot-debugger.pot +0 -0
  464. data/po/rbot-deepthoughts.pot +0 -0
  465. data/po/rbot-delicious.pot +0 -0
  466. data/po/rbot-dice.pot +0 -0
  467. data/po/rbot-dict.pot +0 -0
  468. data/po/rbot-dictclient.pot +112 -0
  469. data/po/rbot-digg.pot +0 -0
  470. data/po/rbot-eightball.pot +0 -0
  471. data/po/rbot-excuse.pot +0 -0
  472. data/po/rbot-factoids.pot +108 -0
  473. data/po/rbot-figlet.pot +37 -0
  474. data/po/rbot-fish.pot +0 -0
  475. data/po/rbot-forecast.pot +0 -0
  476. data/po/rbot-fortune.pot +0 -0
  477. data/po/rbot-freshmeat.pot +0 -0
  478. data/po/rbot-grouphug.pot +36 -0
  479. data/po/rbot-hl2.pot +0 -0
  480. data/po/rbot-host.pot +21 -0
  481. data/po/rbot-imdb.pot +0 -0
  482. data/po/rbot-insult.pot +0 -0
  483. data/po/rbot-iplookup.pot +0 -0
  484. data/po/rbot-karma.pot +0 -0
  485. data/po/rbot-keywords.pot +25 -0
  486. data/po/rbot-lart.pot +0 -0
  487. data/po/rbot-lastfm.pot +190 -0
  488. data/po/rbot-linkbot.pot +0 -0
  489. data/po/rbot-markov.pot +21 -0
  490. data/po/rbot-math.pot +0 -0
  491. data/po/rbot-modes.pot +0 -0
  492. data/po/rbot-nickrecover.pot +37 -0
  493. data/po/rbot-nickserv.pot +105 -0
  494. data/po/rbot-nslookup.pot +0 -0
  495. data/po/rbot-quakeauth.pot +0 -0
  496. data/po/rbot-quiz.pot +0 -0
  497. data/po/rbot-quotes.pot +109 -0
  498. data/po/rbot-reaction.pot +0 -0
  499. data/po/rbot-remind.pot +0 -0
  500. data/po/rbot-remotectl.pot +0 -0
  501. data/po/rbot-ri.pot +0 -0
  502. data/po/rbot-roshambo.pot +0 -0
  503. data/po/rbot-rot13.pot +0 -0
  504. data/po/rbot-roulette.pot +0 -0
  505. data/po/rbot-rss.pot +21 -0
  506. data/po/rbot-salut.pot +0 -0
  507. data/po/rbot-script.pot +0 -0
  508. data/po/rbot-search.pot +0 -0
  509. data/po/rbot-seen.pot +0 -0
  510. data/po/rbot-shiritori.pot +103 -0
  511. data/po/rbot-shortenurls.pot +0 -0
  512. data/po/rbot-slashdot.pot +0 -0
  513. data/po/rbot-spell.pot +55 -0
  514. data/po/rbot-theyfightcrime.pot +0 -0
  515. data/po/rbot-threat.pot +0 -0
  516. data/po/rbot-time.pot +0 -0
  517. data/po/rbot-topic.pot +0 -0
  518. data/po/rbot-translator.pot +78 -0
  519. data/po/rbot-tube.pot +0 -0
  520. data/po/rbot-twitter.pot +25 -0
  521. data/po/rbot-uno.pot +513 -0
  522. data/po/rbot-urban.pot +0 -0
  523. data/po/rbot-url.pot +0 -0
  524. data/po/rbot-usermodes.pot +0 -0
  525. data/po/rbot-wall.pot +33 -0
  526. data/po/rbot-weather.pot +0 -0
  527. data/po/rbot-wheelfortune.pot +206 -0
  528. data/po/rbot-wow.pot +0 -0
  529. data/po/rbot-wserver.pot +0 -0
  530. data/po/rbot-youtube.pot +59 -0
  531. data/po/rbot.pot +1004 -0
  532. data/po/zh_CN/rbot-alias.po +82 -0
  533. data/po/zh_CN/rbot-autoop.po +0 -0
  534. data/po/zh_CN/rbot-autorejoin.po +0 -0
  535. data/po/zh_CN/rbot-azgame.po +186 -0
  536. data/po/zh_CN/rbot-bans.po +0 -0
  537. data/po/zh_CN/rbot-bash.po +0 -0
  538. data/po/zh_CN/rbot-botsnack.po +0 -0
  539. data/po/zh_CN/rbot-cal.po +3 -0
  540. data/po/zh_CN/rbot-chanserv.po +0 -0
  541. data/po/zh_CN/rbot-chucknorris.po +0 -0
  542. data/po/zh_CN/rbot-debugger.po +0 -0
  543. data/po/zh_CN/rbot-deepthoughts.po +0 -0
  544. data/po/zh_CN/rbot-delicious.po +0 -0
  545. data/po/zh_CN/rbot-dice.po +0 -0
  546. data/po/zh_CN/rbot-dict.po +0 -0
  547. data/po/zh_CN/rbot-dictclient.po +111 -0
  548. data/po/zh_CN/rbot-digg.po +0 -0
  549. data/po/zh_CN/rbot-eightball.po +0 -0
  550. data/po/zh_CN/rbot-excuse.po +0 -0
  551. data/po/zh_CN/rbot-factoids.po +107 -0
  552. data/po/zh_CN/rbot-figlet.po +36 -0
  553. data/po/zh_CN/rbot-fish.po +0 -0
  554. data/po/zh_CN/rbot-forecast.po +0 -0
  555. data/po/zh_CN/rbot-fortune.po +0 -0
  556. data/po/zh_CN/rbot-freshmeat.po +0 -0
  557. data/po/zh_CN/rbot-grouphug.po +18 -0
  558. data/po/zh_CN/rbot-hl2.po +0 -0
  559. data/po/zh_CN/rbot-host.po +3 -0
  560. data/po/zh_CN/rbot-imdb.po +0 -0
  561. data/po/zh_CN/rbot-insult.po +0 -0
  562. data/po/zh_CN/rbot-iplookup.po +0 -0
  563. data/po/zh_CN/rbot-karma.po +0 -0
  564. data/po/zh_CN/rbot-keywords.po +24 -0
  565. data/po/zh_CN/rbot-lart.po +0 -0
  566. data/po/zh_CN/rbot-lastfm.po +172 -0
  567. data/po/zh_CN/rbot-linkbot.po +0 -0
  568. data/po/zh_CN/rbot-markov.po +20 -0
  569. data/po/zh_CN/rbot-math.po +0 -0
  570. data/po/zh_CN/rbot-modes.po +0 -0
  571. data/po/zh_CN/rbot-nickrecover.po +36 -0
  572. data/po/zh_CN/rbot-nickserv.po +104 -0
  573. data/po/zh_CN/rbot-nslookup.po +0 -0
  574. data/po/zh_CN/rbot-quakeauth.po +0 -0
  575. data/po/zh_CN/rbot-quiz.po +0 -0
  576. data/po/zh_CN/rbot-quotes.po +108 -0
  577. data/po/zh_CN/rbot-reaction.po +0 -0
  578. data/po/zh_CN/rbot-remind.po +0 -0
  579. data/po/zh_CN/rbot-remotectl.po +0 -0
  580. data/po/zh_CN/rbot-ri.po +0 -0
  581. data/po/zh_CN/rbot-roshambo.po +0 -0
  582. data/po/zh_CN/rbot-rot13.po +0 -0
  583. data/po/zh_CN/rbot-roulette.po +0 -0
  584. data/po/zh_CN/rbot-rss.po +20 -0
  585. data/po/zh_CN/rbot-salut.po +0 -0
  586. data/po/zh_CN/rbot-script.po +0 -0
  587. data/po/zh_CN/rbot-search.po +0 -0
  588. data/po/zh_CN/rbot-seen.po +0 -0
  589. data/po/zh_CN/rbot-shiritori.po +102 -0
  590. data/po/zh_CN/rbot-shortenurls.po +0 -0
  591. data/po/zh_CN/rbot-slashdot.po +0 -0
  592. data/po/zh_CN/rbot-spell.po +54 -0
  593. data/po/zh_CN/rbot-theyfightcrime.po +0 -0
  594. data/po/zh_CN/rbot-threat.po +0 -0
  595. data/po/zh_CN/rbot-time.po +0 -0
  596. data/po/zh_CN/rbot-topic.po +0 -0
  597. data/po/zh_CN/rbot-translator.po +77 -0
  598. data/po/zh_CN/rbot-tube.po +0 -0
  599. data/po/zh_CN/rbot-twitter.po +24 -0
  600. data/po/zh_CN/rbot-uno.po +512 -0
  601. data/po/zh_CN/rbot-urban.po +0 -0
  602. data/po/zh_CN/rbot-url.po +0 -0
  603. data/po/zh_CN/rbot-usermodes.po +0 -0
  604. data/po/zh_CN/rbot-wall.po +33 -0
  605. data/po/zh_CN/rbot-weather.po +0 -0
  606. data/po/zh_CN/rbot-wheelfortune.po +205 -0
  607. data/po/zh_CN/rbot-wow.po +0 -0
  608. data/po/zh_CN/rbot-wserver.po +0 -0
  609. data/po/zh_CN/rbot-youtube.po +58 -0
  610. data/po/zh_CN/rbot.po +1006 -0
  611. data/po/zh_TW/rbot-alias.po +82 -0
  612. data/po/zh_TW/rbot-autoop.po +0 -0
  613. data/po/zh_TW/rbot-autorejoin.po +0 -0
  614. data/po/zh_TW/rbot-azgame.po +186 -0
  615. data/po/zh_TW/rbot-bans.po +0 -0
  616. data/po/zh_TW/rbot-bash.po +0 -0
  617. data/po/zh_TW/rbot-botsnack.po +0 -0
  618. data/po/zh_TW/rbot-cal.po +3 -0
  619. data/po/zh_TW/rbot-chanserv.po +0 -0
  620. data/po/zh_TW/rbot-chucknorris.po +0 -0
  621. data/po/zh_TW/rbot-debugger.po +0 -0
  622. data/po/zh_TW/rbot-deepthoughts.po +0 -0
  623. data/po/zh_TW/rbot-delicious.po +0 -0
  624. data/po/zh_TW/rbot-dice.po +0 -0
  625. data/po/zh_TW/rbot-dict.po +0 -0
  626. data/po/zh_TW/rbot-dictclient.po +111 -0
  627. data/po/zh_TW/rbot-digg.po +0 -0
  628. data/po/zh_TW/rbot-eightball.po +0 -0
  629. data/po/zh_TW/rbot-excuse.po +0 -0
  630. data/po/zh_TW/rbot-factoids.po +107 -0
  631. data/po/zh_TW/rbot-figlet.po +36 -0
  632. data/po/zh_TW/rbot-fish.po +0 -0
  633. data/po/zh_TW/rbot-forecast.po +0 -0
  634. data/po/zh_TW/rbot-fortune.po +0 -0
  635. data/po/zh_TW/rbot-freshmeat.po +0 -0
  636. data/po/zh_TW/rbot-grouphug.po +18 -0
  637. data/po/zh_TW/rbot-hl2.po +0 -0
  638. data/po/zh_TW/rbot-host.po +3 -0
  639. data/po/zh_TW/rbot-imdb.po +0 -0
  640. data/po/zh_TW/rbot-insult.po +0 -0
  641. data/po/zh_TW/rbot-iplookup.po +0 -0
  642. data/po/zh_TW/rbot-karma.po +0 -0
  643. data/po/zh_TW/rbot-keywords.po +24 -0
  644. data/po/zh_TW/rbot-lart.po +0 -0
  645. data/po/zh_TW/rbot-lastfm.po +172 -0
  646. data/po/zh_TW/rbot-linkbot.po +0 -0
  647. data/po/zh_TW/rbot-markov.po +20 -0
  648. data/po/zh_TW/rbot-math.po +0 -0
  649. data/po/zh_TW/rbot-modes.po +0 -0
  650. data/po/zh_TW/rbot-nickrecover.po +36 -0
  651. data/po/zh_TW/rbot-nickserv.po +104 -0
  652. data/po/zh_TW/rbot-nslookup.po +0 -0
  653. data/po/zh_TW/rbot-quakeauth.po +0 -0
  654. data/po/zh_TW/rbot-quiz.po +0 -0
  655. data/po/zh_TW/rbot-quotes.po +108 -0
  656. data/po/zh_TW/rbot-reaction.po +0 -0
  657. data/po/zh_TW/rbot-remind.po +0 -0
  658. data/po/zh_TW/rbot-remotectl.po +0 -0
  659. data/po/zh_TW/rbot-ri.po +0 -0
  660. data/po/zh_TW/rbot-roshambo.po +0 -0
  661. data/po/zh_TW/rbot-rot13.po +0 -0
  662. data/po/zh_TW/rbot-roulette.po +0 -0
  663. data/po/zh_TW/rbot-rss.po +20 -0
  664. data/po/zh_TW/rbot-salut.po +0 -0
  665. data/po/zh_TW/rbot-script.po +0 -0
  666. data/po/zh_TW/rbot-search.po +0 -0
  667. data/po/zh_TW/rbot-seen.po +0 -0
  668. data/po/zh_TW/rbot-shiritori.po +102 -0
  669. data/po/zh_TW/rbot-shortenurls.po +0 -0
  670. data/po/zh_TW/rbot-slashdot.po +0 -0
  671. data/po/zh_TW/rbot-spell.po +54 -0
  672. data/po/zh_TW/rbot-theyfightcrime.po +0 -0
  673. data/po/zh_TW/rbot-threat.po +0 -0
  674. data/po/zh_TW/rbot-time.po +0 -0
  675. data/po/zh_TW/rbot-topic.po +0 -0
  676. data/po/zh_TW/rbot-translator.po +77 -0
  677. data/po/zh_TW/rbot-tube.po +0 -0
  678. data/po/zh_TW/rbot-twitter.po +24 -0
  679. data/po/zh_TW/rbot-uno.po +512 -0
  680. data/po/zh_TW/rbot-urban.po +0 -0
  681. data/po/zh_TW/rbot-url.po +0 -0
  682. data/po/zh_TW/rbot-usermodes.po +0 -0
  683. data/po/zh_TW/rbot-wall.po +33 -0
  684. data/po/zh_TW/rbot-weather.po +0 -0
  685. data/po/zh_TW/rbot-wheelfortune.po +205 -0
  686. data/po/zh_TW/rbot-wow.po +0 -0
  687. data/po/zh_TW/rbot-wserver.po +0 -0
  688. data/po/zh_TW/rbot-youtube.po +58 -0
  689. data/po/zh_TW/rbot.po +1034 -0
  690. data/setup.rb +800 -574
  691. metadata +723 -108
  692. data/data/rbot/contrib/plugins/figlet.rb +0 -20
  693. data/data/rbot/contrib/plugins/ri.rb +0 -83
  694. data/data/rbot/plugins/demauro.rb +0 -95
  695. data/data/rbot/plugins/google.rb +0 -53
  696. data/data/rbot/plugins/opme.rb +0 -19
  697. data/data/rbot/plugins/roshambo.rb +0 -54
  698. data/data/rbot/plugins/rubyurl.rb +0 -39
  699. data/data/rbot/plugins/tinyurl.rb +0 -39
  700. data/data/rbot/plugins/xmlrpc.rb.disabled +0 -52
  701. data/data/rbot/templates/levels.rbot +0 -32
  702. data/data/rbot/templates/users.rbot +0 -1
  703. data/lib/rbot/auth.rb +0 -314
  704. data/lib/rbot/channel.rb +0 -54
  705. data/lib/rbot/httputil.rb +0 -309
  706. data/lib/rbot/utils.rb +0 -83
@@ -0,0 +1,6 @@
1
+ module Irc
2
+ module PKGConfig
3
+ DATADIR = '/home/oblomov/share/rbot'
4
+ COREDIR = '/home/oblomov/lib/ruby/rbot/core'
5
+ end
6
+ end
@@ -1,21 +1,36 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: rbot plugin management
5
+
6
+ require 'singleton'
7
+
1
8
  module Irc
2
- BotConfig.register BotConfigArrayValue.new('plugins.blacklist',
3
- :default => [], :wizard => false, :requires_restart => true,
9
+ class Bot
10
+ Config.register Config::ArrayValue.new('plugins.blacklist',
11
+ :default => [], :wizard => false, :requires_rescan => true,
4
12
  :desc => "Plugins that should not be loaded")
5
13
  module Plugins
6
14
  require 'rbot/messagemapper'
7
15
 
8
- =begin
9
- base class for all rbot plugins
10
- certain methods will be called if they are provided, if you define one of
11
- the following methods, it will be called as appropriate:
16
+ =begin rdoc
17
+ BotModule is the base class for the modules that enhance the rbot
18
+ functionality. Rather than subclassing BotModule, however, one should
19
+ subclass either CoreBotModule (reserved for system modules) or Plugin
20
+ (for user plugins).
21
+
22
+ A BotModule interacts with Irc events by defining one or more of the following
23
+ methods, which get called as appropriate when the corresponding Irc event
24
+ happens.
12
25
 
13
26
  map(template, options)::
14
27
  map!(template, options)::
15
- map is the new, cleaner way to respond to specific message formats
16
- without littering your plugin code with regexps. The difference
17
- between map and map! is that map! will not register the new command
18
- as an alternative name for the plugin.
28
+ map is the new, cleaner way to respond to specific message formats without
29
+ littering your plugin code with regexps, and should be used instead of
30
+ #register() and #privmsg() (see below) when possible.
31
+
32
+ The difference between map and map! is that map! will not register the new
33
+ command as an alternative name for the plugin.
19
34
 
20
35
  Examples:
21
36
 
@@ -47,9 +62,11 @@ module Plugins
47
62
  plugin.map 'karmastats', :auth => 'karma'
48
63
 
49
64
  # maps can be restricted to public or private message:
50
- plugin.map 'karmastats', :private false,
51
- plugin.map 'karmastats', :public false,
52
- end
65
+ plugin.map 'karmastats', :private => false
66
+ plugin.map 'karmastats', :public => false
67
+
68
+ See MessageMapper#map for more information on the template format and the
69
+ allowed options.
53
70
 
54
71
  listen(UserMessage)::
55
72
  Called for all messages of any type. To
@@ -58,16 +75,38 @@ module Plugins
58
75
  QuitMessage, PartMessage, JoinMessage, NickMessage,
59
76
  etc.
60
77
 
78
+ ctcp_listen(UserMessage)::
79
+ Called for all messages that contain a CTCP command.
80
+ Use message.ctcp to get the CTCP command, and
81
+ message.message to get the parameter string. To reply,
82
+ use message.ctcp_reply, which sends a private NOTICE
83
+ to the sender.
84
+
85
+ message(PrivMessage)::
86
+ Called for all PRIVMSG. Hook on this method if you
87
+ need to handle PRIVMSGs regardless of whether they are
88
+ addressed to the bot or not, and regardless of
89
+
61
90
  privmsg(PrivMessage)::
62
- called for a PRIVMSG if the first word matches one
63
- the plugin register()d for. Use m.plugin to get
91
+ Called for a PRIVMSG if the first word matches one
92
+ the plugin #register()ed for. Use m.plugin to get
64
93
  that word and m.params for the rest of the message,
65
94
  if applicable.
66
95
 
96
+ unreplied(PrivMessage)::
97
+ Called for a PRIVMSG which has not been replied to.
98
+
99
+ notice(NoticeMessage)::
100
+ Called for all Notices. Please notice that in general
101
+ should not be replied to.
102
+
67
103
  kick(KickMessage)::
68
104
  Called when a user (or the bot) is kicked from a
69
105
  channel the bot is in.
70
106
 
107
+ invite(InviteMessage)::
108
+ Called when the bot is invited to a channel.
109
+
71
110
  join(JoinMessage)::
72
111
  Called when a user (or the bot) joins a channel
73
112
 
@@ -79,13 +118,27 @@ module Plugins
79
118
 
80
119
  nick(NickMessage)::
81
120
  Called when a user (or the bot) changes Nick
121
+ modechange(ModeChangeMessage)::
122
+ Called when a User or Channel mode is changed
82
123
  topic(TopicMessage)::
83
124
  Called when a user (or the bot) changes a channel
84
125
  topic
85
126
 
86
- connect():: Called when a server is joined successfully, but
127
+ welcome(WelcomeMessage)::
128
+ Called when the welcome message is received on
129
+ joining a server succesfully.
130
+
131
+ motd(MotdMessage)::
132
+ Called when the Message Of The Day is fully
133
+ recevied from the server.
134
+
135
+ connect:: Called when a server is joined successfully, but
87
136
  before autojoin channels are joined (no params)
88
137
 
138
+ set_language(String)::
139
+ Called when the user sets a new language
140
+ whose name is the given String
141
+
89
142
  save:: Called when you are required to save your plugin's
90
143
  state, if you maintain data between sessions
91
144
 
@@ -94,62 +147,162 @@ module Plugins
94
147
  files/connections or flush caches here
95
148
  =end
96
149
 
97
- class Plugin
98
- attr_reader :bot # the associated bot
99
- # initialise your plugin. Always call super if you override this method,
100
- # as important variables are set up for you
150
+ class BotModule
151
+ # the associated bot
152
+ attr_reader :bot
153
+
154
+ # the plugin registry
155
+ attr_reader :registry
156
+
157
+ # the message map handler
158
+ attr_reader :handler
159
+
160
+ # Initialise your bot module. Always call super if you override this method,
161
+ # as important variables are set up for you:
162
+ #
163
+ # @bot::
164
+ # the rbot instance
165
+ # @registry::
166
+ # the botmodule's registry, which can be used to store permanent data
167
+ # (see Registry::Accessor for additional documentation)
168
+ #
169
+ # Other instance variables which are defined and should not be overwritten
170
+ # byt the user, but aren't usually accessed directly, are:
171
+ #
172
+ # @manager::
173
+ # the plugins manager instance
174
+ # @botmodule_triggers::
175
+ # an Array of words this plugin #register()ed itself for
176
+ # @handler::
177
+ # the MessageMapper that handles this plugin's maps
178
+ #
101
179
  def initialize
102
- @bot = Plugins.bot
103
- @names = Array.new
180
+ @manager = Plugins::manager
181
+ @bot = @manager.bot
182
+ @priority = nil
183
+
184
+ @botmodule_triggers = Array.new
185
+
104
186
  @handler = MessageMapper.new(self)
105
- @registry = BotRegistryAccessor.new(@bot, self.class.to_s.gsub(/^.*::/, ""))
187
+ @registry = Registry::Accessor.new(@bot, self.class.to_s.gsub(/^.*::/, ""))
188
+
189
+ @manager.add_botmodule(self)
190
+ if self.respond_to?('set_language')
191
+ self.set_language(@bot.lang.language)
192
+ end
106
193
  end
107
194
 
195
+ # Changing the value of @priority directly will cause problems,
196
+ # Please use priority=.
197
+ def priority
198
+ @priority ||= 1
199
+ end
200
+
201
+ # Returns the symbol :BotModule
202
+ def botmodule_class
203
+ :BotModule
204
+ end
205
+
206
+ # Method called to flush the registry, thus ensuring that the botmodule's permanent
207
+ # data is committed to disk
208
+ #
108
209
  def flush_registry
109
210
  # debug "Flushing #{@registry}"
110
211
  @registry.flush
111
212
  end
112
213
 
214
+ # Method called to cleanup before the plugin is unloaded. If you overload
215
+ # this method to handle additional cleanup tasks, remember to call super()
216
+ # so that the default cleanup actions are taken care of as well.
217
+ #
113
218
  def cleanup
114
219
  # debug "Closing #{@registry}"
115
220
  @registry.close
116
221
  end
117
222
 
223
+ # Handle an Irc::PrivMessage for which this BotModule has a map. The method
224
+ # is called automatically and there is usually no need to call it
225
+ # explicitly.
226
+ #
118
227
  def handle(m)
119
228
  @handler.handle(m)
120
229
  end
121
230
 
231
+ # Signal to other BotModules that an even happened.
232
+ #
233
+ def call_event(ev, *args)
234
+ @bot.plugins.delegate('event_' + ev.to_s.gsub(/[^\w\?!]+/, '_'), *(args.push Hash.new))
235
+ end
236
+
237
+ # call-seq: map(template, options)
238
+ #
239
+ # This is the preferred way to register the BotModule so that it
240
+ # responds to appropriately-formed messages on Irc.
241
+ #
122
242
  def map(*args)
123
- @handler.map(*args)
124
- # register this map
125
- name = @handler.last.items[0]
126
- self.register name
127
- unless self.respond_to?('privmsg')
128
- def self.privmsg(m)
129
- handle(m)
130
- end
131
- end
243
+ do_map(false, *args)
132
244
  end
133
245
 
246
+ # call-seq: map!(template, options)
247
+ #
248
+ # This is the same as map but doesn't register the new command
249
+ # as an alternative name for the plugin.
250
+ #
134
251
  def map!(*args)
135
- @handler.map(*args)
252
+ do_map(true, *args)
253
+ end
254
+
255
+ # Auxiliary method called by #map and #map!
256
+ def do_map(silent, *args)
257
+ @handler.map(self, *args)
136
258
  # register this map
137
- name = @handler.last.items[0]
138
- self.register name, {:hidden => true}
259
+ map = @handler.last
260
+ name = map.items[0]
261
+ self.register name, :auth => nil, :hidden => silent
262
+ @manager.register_map(self, map)
139
263
  unless self.respond_to?('privmsg')
140
- def self.privmsg(m)
264
+ def self.privmsg(m) #:nodoc:
141
265
  handle(m)
142
266
  end
143
267
  end
144
268
  end
145
269
 
146
- # return an identifier for this plugin, defaults to a list of the message
270
+ # Sets the default auth for command path _cmd_ to _val_ on channel _chan_:
271
+ # usually _chan_ is either "*" for everywhere, public and private (in which
272
+ # case it can be omitted) or "?" for private communications
273
+ #
274
+ def default_auth(cmd, val, chan="*")
275
+ case cmd
276
+ when "*", ""
277
+ c = nil
278
+ else
279
+ c = cmd
280
+ end
281
+ Auth::defaultbotuser.set_default_permission(propose_default_path(c), val)
282
+ end
283
+
284
+ # Gets the default command path which would be given to command _cmd_
285
+ def propose_default_path(cmd)
286
+ [name, cmd].compact.join("::")
287
+ end
288
+
289
+ # Return an identifier for this plugin, defaults to a list of the message
147
290
  # prefixes handled (used for error messages etc)
148
291
  def name
149
- @names.join("|")
292
+ self.class.to_s.downcase.sub(/^#<module:.*?>::/,"").sub(/(plugin|module)?$/,"")
293
+ end
294
+
295
+ # Just calls name
296
+ def to_s
297
+ name
298
+ end
299
+
300
+ # Intern the name
301
+ def to_sym
302
+ self.name.to_sym
150
303
  end
151
304
 
152
- # return a help string for your module. for complex modules, you may wish
305
+ # Return a help string for your module. For complex modules, you may wish
153
306
  # to break your help into topics, and return a list of available topics if
154
307
  # +topic+ is nil. +plugin+ is passed containing the matching prefix for
155
308
  # this message - if your plugin handles multiple prefixes, make sure you
@@ -158,67 +311,290 @@ module Plugins
158
311
  "no help"
159
312
  end
160
313
 
161
- # register the plugin as a handler for messages prefixed +name+
162
- # this can be called multiple times for a plugin to handle multiple
163
- # message prefixes
164
- def register(name,opts={})
314
+ # Register the plugin as a handler for messages prefixed _cmd_.
315
+ #
316
+ # This can be called multiple times for a plugin to handle multiple message
317
+ # prefixes.
318
+ #
319
+ # This command is now superceded by the #map() command, which should be used
320
+ # instead whenever possible.
321
+ #
322
+ def register(cmd, opts={})
165
323
  raise ArgumentError, "Second argument must be a hash!" unless opts.kind_of?(Hash)
166
- return if Plugins.plugins.has_key?(name)
167
- Plugins.plugins[name] = self
168
- @names << name unless opts.fetch(:hidden, false)
324
+ who = @manager.who_handles?(cmd)
325
+ if who
326
+ raise "Command #{cmd} is already handled by #{who.botmodule_class} #{who}" if who != self
327
+ return
328
+ end
329
+ if opts.has_key?(:auth)
330
+ @manager.register(self, cmd, opts[:auth])
331
+ else
332
+ @manager.register(self, cmd, propose_default_path(cmd))
333
+ end
334
+ @botmodule_triggers << cmd unless opts.fetch(:hidden, false)
169
335
  end
170
336
 
171
- # default usage method provided as a utility for simple plugins. The
337
+ # Default usage method provided as a utility for simple plugins. The
172
338
  # MessageMapper uses 'usage' as its default fallback method.
339
+ #
173
340
  def usage(m, params = {})
174
- m.reply "incorrect usage, ask for help using '#{@bot.nick}: help #{m.plugin}'"
341
+ m.reply(_("incorrect usage, ask for help using '%{command}'") % {:command => "#{@bot.nick}: help #{m.plugin}"})
342
+ end
343
+
344
+ # Define the priority of the module. During event delegation, lower
345
+ # priority modules will be called first. Default priority is 1
346
+ def priority=(prio)
347
+ if @priority != prio
348
+ @priority = prio
349
+ @bot.plugins.mark_priorities_dirty
350
+ end
351
+ end
352
+ end
353
+
354
+ # A CoreBotModule is a BotModule that provides core functionality.
355
+ #
356
+ # This class should not be used by user plugins, as it's reserved for system
357
+ # plugins such as the ones that handle authentication, configuration and basic
358
+ # functionality.
359
+ #
360
+ class CoreBotModule < BotModule
361
+ def botmodule_class
362
+ :CoreBotModule
175
363
  end
364
+ end
176
365
 
366
+ # A Plugin is a BotModule that provides additional functionality.
367
+ #
368
+ # A user-defined plugin should subclass this, and then define any of the
369
+ # methods described in the documentation for BotModule to handle interaction
370
+ # with Irc events.
371
+ #
372
+ class Plugin < BotModule
373
+ def botmodule_class
374
+ :Plugin
375
+ end
177
376
  end
178
377
 
179
- # class to manage multiple plugins and delegate messages to them for
378
+ # Singleton to manage multiple plugins and delegate messages to them for
180
379
  # handling
181
- class Plugins
182
- # hash of registered message prefixes and associated plugins
183
- @@plugins = Hash.new
184
- # associated IrcBot class
185
- @@bot = nil
186
-
187
- # bot:: associated IrcBot class
188
- # dirlist:: array of directories to scan (in order) for plugins
380
+ class PluginManagerClass
381
+ include Singleton
382
+ attr_reader :bot
383
+ attr_reader :botmodules
384
+ attr_reader :maps
385
+
386
+ # This is the list of patterns commonly delegated to plugins.
387
+ # A fast delegation lookup is enabled for them.
388
+ DEFAULT_DELEGATE_PATTERNS = %r{^(?:
389
+ connect|names|nick|
390
+ listen|ctcp_listen|privmsg|unreplied|
391
+ kick|join|part|quit|
392
+ save|cleanup|flush_registry|
393
+ set_.*|event_.*
394
+ )$}x
395
+
396
+ def initialize
397
+ @botmodules = {
398
+ :CoreBotModule => [],
399
+ :Plugin => []
400
+ }
401
+
402
+ @names_hash = Hash.new
403
+ @commandmappers = Hash.new
404
+ @maps = Hash.new
405
+
406
+ # modules will be sorted on first delegate call
407
+ @sorted_modules = nil
408
+
409
+ @delegate_list = Hash.new { |h, k|
410
+ h[k] = Array.new
411
+ }
412
+
413
+ @dirs = []
414
+
415
+ @failed = Array.new
416
+ @ignored = Array.new
417
+
418
+ bot_associate(nil)
419
+ end
420
+
421
+ def inspect
422
+ ret = self.to_s[0..-2]
423
+ ret << ' corebotmodules='
424
+ ret << @botmodules[:CoreBotModule].map { |m|
425
+ m.name
426
+ }.inspect
427
+ ret << ' plugins='
428
+ ret << @botmodules[:Plugin].map { |m|
429
+ m.name
430
+ }.inspect
431
+ ret << ">"
432
+ end
433
+
434
+ # Reset lists of botmodules
435
+ def reset_botmodule_lists
436
+ @botmodules[:CoreBotModule].clear
437
+ @botmodules[:Plugin].clear
438
+ @names_hash.clear
439
+ @commandmappers.clear
440
+ @maps.clear
441
+ @failures_shown = false
442
+ mark_priorities_dirty
443
+ end
444
+
445
+ # Associate with bot _bot_
446
+ def bot_associate(bot)
447
+ reset_botmodule_lists
448
+ @bot = bot
449
+ end
450
+
451
+ # Returns the botmodule with the given _name_
452
+ def [](name)
453
+ @names_hash[name.to_sym]
454
+ end
455
+
456
+ # Returns +true+ if _cmd_ has already been registered as a command
457
+ def who_handles?(cmd)
458
+ return nil unless @commandmappers.has_key?(cmd.to_sym)
459
+ return @commandmappers[cmd.to_sym][:botmodule]
460
+ end
461
+
462
+ # Registers botmodule _botmodule_ with command _cmd_ and command path _auth_path_
463
+ def register(botmodule, cmd, auth_path)
464
+ raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
465
+ @commandmappers[cmd.to_sym] = {:botmodule => botmodule, :auth => auth_path}
466
+ end
467
+
468
+ # Registers botmodule _botmodule_ with map _map_. This adds the map to the #maps hash
469
+ # which has three keys:
189
470
  #
190
- # create a new plugin handler, scanning for plugins in +dirlist+
191
- def initialize(bot, dirlist)
192
- @@bot = bot
193
- @dirs = dirlist
194
- scan
471
+ # botmodule:: the associated botmodule
472
+ # auth:: an array of auth keys checked by the map; the first is the full_auth_path of the map
473
+ # map:: the actual MessageTemplate object
474
+ #
475
+ #
476
+ def register_map(botmodule, map)
477
+ raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
478
+ @maps[map.template] = { :botmodule => botmodule, :auth => [map.options[:full_auth_path]], :map => map }
479
+ end
480
+
481
+ def add_botmodule(botmodule)
482
+ raise TypeError, "Argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
483
+ kl = botmodule.botmodule_class
484
+ if @names_hash.has_key?(botmodule.to_sym)
485
+ case self[botmodule].botmodule_class
486
+ when kl
487
+ raise "#{kl} #{botmodule} already registered!"
488
+ else
489
+ raise "#{self[botmodule].botmodule_class} #{botmodule} already registered, cannot re-register as #{kl}"
490
+ end
491
+ end
492
+ @botmodules[kl] << botmodule
493
+ @names_hash[botmodule.to_sym] = botmodule
494
+ mark_priorities_dirty
495
+ end
496
+
497
+ # Returns an array of the loaded plugins
498
+ def core_modules
499
+ @botmodules[:CoreBotModule]
500
+ end
501
+
502
+ # Returns an array of the loaded plugins
503
+ def plugins
504
+ @botmodules[:Plugin]
505
+ end
506
+
507
+ # Returns a hash of the registered message prefixes and associated
508
+ # plugins
509
+ def commands
510
+ @commandmappers
195
511
  end
196
512
 
197
- # access to associated bot
198
- def Plugins.bot
199
- @@bot
513
+ # Tells the PluginManager that the next time it delegates an event, it
514
+ # should sort the modules by priority
515
+ def mark_priorities_dirty
516
+ @sorted_modules = nil
200
517
  end
201
518
 
202
- # access to list of plugins
203
- def Plugins.plugins
204
- @@plugins
519
+ # Makes a string of error _err_ by adding text _str_
520
+ def report_error(str, err)
521
+ ([str, err.inspect] + err.backtrace).join("\n")
522
+ end
523
+
524
+ # This method is the one that actually loads a module from the
525
+ # file _fname_
526
+ #
527
+ # _desc_ is a simple description of what we are loading (plugin/botmodule/whatever)
528
+ #
529
+ # It returns the Symbol :loaded on success, and an Exception
530
+ # on failure
531
+ #
532
+ def load_botmodule_file(fname, desc=nil)
533
+ # create a new, anonymous module to "house" the plugin
534
+ # the idea here is to prevent namespace pollution. perhaps there
535
+ # is another way?
536
+ plugin_module = Module.new
537
+ # each plugin uses its own textdomain, we bind it automatically here
538
+ bindtextdomain_to(plugin_module, "rbot-#{File.basename(fname, '.rb')}")
539
+
540
+ desc = desc.to_s + " " if desc
541
+
542
+ begin
543
+ plugin_string = IO.read(fname)
544
+ debug "loading #{desc}#{fname}"
545
+ plugin_module.module_eval(plugin_string, fname)
546
+ return :loaded
547
+ rescue Exception => err
548
+ # rescue TimeoutError, StandardError, NameError, LoadError, SyntaxError => err
549
+ error report_error("#{desc}#{fname} load failed", err)
550
+ bt = err.backtrace.select { |line|
551
+ line.match(/^(\(eval\)|#{fname}):\d+/)
552
+ }
553
+ bt.map! { |el|
554
+ el.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
555
+ "#{fname}#{$1}#{$3}"
556
+ }
557
+ }
558
+ msg = err.to_str.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
559
+ "#{fname}#{$1}#{$3}"
560
+ }
561
+ newerr = err.class.new(msg)
562
+ newerr.set_backtrace(bt)
563
+ return newerr
564
+ end
565
+ end
566
+ private :load_botmodule_file
567
+
568
+ # add one or more directories to the list of directories to
569
+ # load botmodules from
570
+ #
571
+ # TODO find a way to specify necessary plugins which _must_ be loaded
572
+ #
573
+ def add_botmodule_dir(*dirlist)
574
+ @dirs += dirlist
575
+ debug "Botmodule loading path: #{@dirs.join(', ')}"
576
+ end
577
+
578
+ def clear_botmodule_dirs
579
+ @dirs.clear
580
+ debug "Botmodule loading path cleared"
205
581
  end
206
582
 
207
583
  # load plugins from pre-assigned list of directories
208
584
  def scan
209
- @failed = Array.new
210
- @ignored = Array.new
585
+ @failed.clear
586
+ @ignored.clear
587
+ @delegate_list.clear
588
+
211
589
  processed = Hash.new
212
590
 
213
- @@bot.config['plugins.blacklist'].each { |p|
591
+ @bot.config['plugins.blacklist'].each { |p|
214
592
  pn = p + ".rb"
215
593
  processed[pn.intern] = :blacklisted
216
594
  }
217
595
 
218
- dirs = Array.new
219
- dirs << Config::datadir + "/plugins"
220
- dirs += @dirs
221
- dirs.reverse.each {|dir|
596
+ dirs = @dirs
597
+ dirs.each {|dir|
222
598
  if(FileTest.directory?(dir))
223
599
  d = Dir.new(dir)
224
600
  d.sort.each {|file|
@@ -242,43 +618,24 @@ module Plugins
242
618
 
243
619
  next unless(file =~ /\.rb$/)
244
620
 
245
- tmpfilename = "#{dir}/#{file}"
246
-
247
- # create a new, anonymous module to "house" the plugin
248
- # the idea here is to prevent namespace pollution. perhaps there
249
- # is another way?
250
- plugin_module = Module.new
251
-
252
- begin
253
- plugin_string = IO.readlines(tmpfilename).join("")
254
- debug "loading plugin #{tmpfilename}"
255
- plugin_module.module_eval(plugin_string, tmpfilename)
256
- processed[file.intern] = :loaded
257
- rescue Exception => err
258
- # rescue TimeoutError, StandardError, NameError, LoadError, SyntaxError => err
259
- warning "plugin #{tmpfilename} load failed\n" + err.inspect
260
- debug err.backtrace.join("\n")
261
- bt = err.backtrace.select { |line|
262
- line.match(/^(\(eval\)|#{tmpfilename}):\d+/)
263
- }
264
- bt.map! { |el|
265
- el.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
266
- "#{tmpfilename}#{$1}#{$3}"
267
- }
268
- }
269
- msg = err.to_str.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
270
- "#{tmpfilename}#{$1}#{$3}"
271
- }
272
- newerr = err.class.new(msg)
273
- newerr.set_backtrace(bt)
274
- # debug "Simplified error: " << newerr.inspect
275
- # debug newerr.backtrace.join("\n")
276
- @failed << { :name => file, :dir => dir, :reason => newerr }
277
- # debug "Failures: #{@failed.inspect}"
621
+ did_it = load_botmodule_file("#{dir}/#{file}", "plugin")
622
+ case did_it
623
+ when Symbol
624
+ processed[file.intern] = did_it
625
+ when Exception
626
+ @failed << { :name => file, :dir => dir, :reason => did_it }
278
627
  end
628
+
279
629
  }
280
630
  end
281
631
  }
632
+ debug "finished loading plugins: #{status(true)}"
633
+ (core_modules + plugins).each { |p|
634
+ p.methods.grep(DEFAULT_DELEGATE_PATTERNS).each { |m|
635
+ @delegate_list[m.intern] << p
636
+ }
637
+ }
638
+ mark_priorities_dirty
282
639
  end
283
640
 
284
641
  # call the save method for each active plugin
@@ -290,6 +647,7 @@ module Plugins
290
647
  # call the cleanup method for each active plugin
291
648
  def cleanup
292
649
  delegate 'cleanup'
650
+ reset_botmodule_lists
293
651
  end
294
652
 
295
653
  # drop all plugins and rescan plugins on disk
@@ -297,43 +655,84 @@ module Plugins
297
655
  def rescan
298
656
  save
299
657
  cleanup
300
- @@plugins = Hash.new
301
658
  scan
302
-
303
659
  end
304
660
 
305
661
  def status(short=false)
662
+ output = []
663
+ if self.core_length > 0
664
+ if short
665
+ output << n_("%{count} core module loaded", "%{count} core modules loaded",
666
+ self.core_length) % {:count => self.core_length}
667
+ else
668
+ output << n_("%{count} core module: %{list}",
669
+ "%{count} core modules: %{list}", self.core_length) %
670
+ { :count => self.core_length,
671
+ :list => core_modules.collect{ |p| p.name}.sort.join(", ") }
672
+ end
673
+ else
674
+ output << _("no core botmodules loaded")
675
+ end
306
676
  # Active plugins first
307
- if(@@plugins.length > 0)
308
- list = "#{length} plugin#{'s' if length > 1}"
677
+ if(self.length > 0)
309
678
  if short
310
- list << " loaded"
679
+ output << n_("%{count} plugin loaded", "%{count} plugins loaded",
680
+ self.length) % {:count => self.length}
311
681
  else
312
- list << ": " + @@plugins.values.uniq.collect{|p| p.name}.sort.join(", ")
682
+ output << n_("%{count} plugin: %{list}",
683
+ "%{count} plugins: %{list}", self.length) %
684
+ { :count => self.length,
685
+ :list => plugins.collect{ |p| p.name}.sort.join(", ") }
313
686
  end
314
687
  else
315
- list = "no plugins active"
688
+ output << "no plugins active"
316
689
  end
317
690
  # Ignored plugins next
318
- unless @ignored.empty?
319
- list << "; #{Underline}#{@ignored.length} plugin#{'s' if @ignored.length > 1} ignored#{Underline}"
320
- list << ": use #{Bold}help ignored plugins#{Bold} to see why" unless short
691
+ unless @ignored.empty? or @failures_shown
692
+ if short
693
+ output << n_("%{highlight}%{count} plugin ignored%{highlight}",
694
+ "%{highlight}%{count} plugins ignored%{highlight}",
695
+ @ignored.length) %
696
+ { :count => @ignored.length, :highlight => Underline }
697
+ else
698
+ output << n_("%{highlight}%{count} plugin ignored%{highlight}: use %{bold}%{command}%{bold} to see why",
699
+ "%{highlight}%{count} plugins ignored%{highlight}: use %{bold}%{command}%{bold} to see why",
700
+ @ignored.length) %
701
+ { :count => @ignored.length, :highlight => Underline,
702
+ :bold => Bold, :command => "help ignored plugins"}
703
+ end
321
704
  end
322
705
  # Failed plugins next
323
- unless @failed.empty?
324
- list << "; #{Reverse}#{@failed.length} plugin#{'s' if @failed.length > 1} failed to load#{Reverse}"
325
- list << ": use #{Bold}help failed plugins#{Bold} to see why" unless short
706
+ unless @failed.empty? or @failures_shown
707
+ if short
708
+ output << n_("%{highlight}%{count} plugin failed to load%{highlight}",
709
+ "%{highlight}%{count} plugins failed to load%{highlight}",
710
+ @failed.length) %
711
+ { :count => @failed.length, :highlight => Reverse }
712
+ else
713
+ output << n_("%{highlight}%{count} plugin failed to load%{highlight}: use %{bold}%{command}%{bold} to see why",
714
+ "%{highlight}%{count} plugins failed to load%{highlight}: use %{bold}%{command}%{bold} to see why",
715
+ @failed.length) %
716
+ { :count => @failed.length, :highlight => Reverse,
717
+ :bold => Bold, :command => "help failed plugins"}
718
+ end
326
719
  end
327
- list
720
+ output.join '; '
328
721
  end
329
722
 
330
723
  # return list of help topics (plugin names)
331
724
  def helptopics
332
- return " [#{status}]"
725
+ rv = status
726
+ @failures_shown = true
727
+ rv
333
728
  end
334
729
 
335
730
  def length
336
- @@plugins.values.uniq.length
731
+ plugins.length
732
+ end
733
+
734
+ def core_length
735
+ core_modules.length
337
736
  end
338
737
 
339
738
  # return help for +topic+ (call associated plugin's help method)
@@ -341,80 +740,222 @@ module Plugins
341
740
  case topic
342
741
  when /fail(?:ed)?\s*plugins?.*(trace(?:back)?s?)?/
343
742
  # debug "Failures: #{@failed.inspect}"
344
- return "no plugins failed to load" if @failed.empty?
345
- return (@failed.inject(Array.new) { |list, p|
346
- list << "#{Bold}#{p[:name]}#{Bold} in #{p[:dir]} failed"
347
- list << "with error #{p[:reason].class}: #{p[:reason]}"
348
- list << "at #{p[:reason].backtrace.join(', ')}" if $1 and not p[:reason].backtrace.empty?
349
- list
350
- }).join("\n")
743
+ return _("no plugins failed to load") if @failed.empty?
744
+ return @failed.collect { |p|
745
+ _('%{highlight}%{plugin}%{highlight} in %{dir} failed with error %{exception}: %{reason}') % {
746
+ :highlight => Bold, :plugin => p[:name], :dir => p[:dir],
747
+ :exception => p[:reason].class, :reason => p[:reason],
748
+ } + if $1 && !p[:reason].backtrace.empty?
749
+ _('at %{backtrace}') % {:backtrace => p[:reason].backtrace.join(', ')}
750
+ else
751
+ ''
752
+ end
753
+ }.join("\n")
351
754
  when /ignored?\s*plugins?/
352
- return "no plugins were ignored" if @ignored.empty?
353
- return (@ignored.inject(Array.new) { |list, p|
354
- case p[:reason]
355
- when :loaded
356
- list << "#{p[:name]} in #{p[:dir]} (overruled by previous)"
357
- else
358
- list << "#{p[:name]} in #{p[:dir]} (#{p[:reason].to_s})"
359
- end
360
- list
361
- }).join(", ")
755
+ return _('no plugins were ignored') if @ignored.empty?
756
+
757
+ tmp = Hash.new
758
+ @ignored.each do |p|
759
+ reason = p[:loaded] ? _('overruled by previous') : _(p[:reason].to_s)
760
+ ((tmp[p[:dir]] ||= Hash.new)[reason] ||= Array.new).push(p[:name])
761
+ end
762
+
763
+ return tmp.map do |dir, reasons|
764
+ # FIXME get rid of these string concatenations to make gettext easier
765
+ s = reasons.map { |r, list|
766
+ list.map { |_| _.sub(/\.rb$/, '') }.join(', ') + " (#{r})"
767
+ }.join('; ')
768
+ "in #{dir}: #{s}"
769
+ end.join('; ')
362
770
  when /^(\S+)\s*(.*)$/
363
771
  key = $1
364
772
  params = $2
365
- if(@@plugins.has_key?(key))
773
+
774
+ # Let's see if we can match a plugin by the given name
775
+ (core_modules + plugins).each { |p|
776
+ next unless p.name == key
366
777
  begin
367
- return @@plugins[key].help(key, params)
778
+ return p.help(key, params)
368
779
  rescue Exception => err
369
780
  #rescue TimeoutError, StandardError, NameError, SyntaxError => err
370
- error "plugin #{@@plugins[key].name} help() failed: #{err.class}: #{err}"
371
- error err.backtrace.join("\n")
781
+ error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
782
+ end
783
+ }
784
+
785
+ # Nope, let's see if it's a command, and ask for help at the corresponding botmodule
786
+ k = key.to_sym
787
+ if commands.has_key?(k)
788
+ p = commands[k][:botmodule]
789
+ begin
790
+ return p.help(key, params)
791
+ rescue Exception => err
792
+ #rescue TimeoutError, StandardError, NameError, SyntaxError => err
793
+ error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
372
794
  end
373
- else
374
- return false
375
795
  end
376
796
  end
797
+ return false
798
+ end
799
+
800
+ def sort_modules
801
+ @sorted_modules = (core_modules + plugins).sort do |a, b|
802
+ a.priority <=> b.priority
803
+ end || []
804
+
805
+ @delegate_list.each_value do |list|
806
+ list.sort! {|a,b| a.priority <=> b.priority}
807
+ end
377
808
  end
378
809
 
379
- # see if each plugin handles +method+, and if so, call it, passing
380
- # +message+ as a parameter
810
+ # call-seq: delegate</span><span class="method-args">(method, m, opts={})</span>
811
+ # <span class="method-name">delegate</span><span class="method-args">(method, opts={})
812
+ #
813
+ # see if each plugin handles _method_, and if so, call it, passing
814
+ # _m_ as a parameter (if present). BotModules are called in order of
815
+ # priority from lowest to highest.
816
+ #
817
+ # If the passed _m_ is a BasicUserMessage and is marked as #ignored?, it
818
+ # will only be delegated to plugins with negative priority. Conversely, if
819
+ # it's a fake message (see BotModule#fake_message), it will only be
820
+ # delegated to plugins with positive priority.
821
+ #
822
+ # Note that _m_ can also be an exploded Array, but in this case the last
823
+ # element of it cannot be a Hash, or it will be interpreted as the options
824
+ # Hash for delegate itself. The last element can be a subclass of a Hash, though.
825
+ # To be on the safe side, you can add an empty Hash as last parameter for delegate
826
+ # when calling it with an exploded Array:
827
+ # @bot.plugins.delegate(method, *(args.push Hash.new))
828
+ #
829
+ # Currently supported options are the following:
830
+ # :above ::
831
+ # if specified, the delegation will only consider plugins with a priority
832
+ # higher than the specified value
833
+ # :below ::
834
+ # if specified, the delegation will only consider plugins with a priority
835
+ # lower than the specified value
836
+ #
381
837
  def delegate(method, *args)
382
- @@plugins.values.uniq.each {|p|
383
- if(p.respond_to? method)
838
+ # if the priorities order of the delegate list is dirty,
839
+ # meaning some modules have been added or priorities have been
840
+ # changed, then the delegate list will need to be sorted before
841
+ # delegation. This should always be true for the first delegation.
842
+ sort_modules unless @sorted_modules
843
+
844
+ opts = {}
845
+ opts.merge(args.pop) if args.last.class == Hash
846
+
847
+ m = args.first
848
+ if BasicUserMessage === m
849
+ # ignored messages should not be delegated
850
+ # to plugins with positive priority
851
+ opts[:below] ||= 0 if m.ignored?
852
+ # fake messages should not be delegated
853
+ # to plugins with negative priority
854
+ opts[:above] ||= 0 if m.recurse_depth > 0
855
+ end
856
+
857
+ above = opts[:above]
858
+ below = opts[:below]
859
+
860
+ # debug "Delegating #{method.inspect}"
861
+ ret = Array.new
862
+ if method.match(DEFAULT_DELEGATE_PATTERNS)
863
+ debug "fast-delegating #{method}"
864
+ m = method.to_sym
865
+ debug "no-one to delegate to" unless @delegate_list.has_key?(m)
866
+ return [] unless @delegate_list.has_key?(m)
867
+ @delegate_list[m].each { |p|
384
868
  begin
385
- p.send method, *args
869
+ prio = p.priority
870
+ unless (above and above >= prio) or (below and below <= prio)
871
+ ret.push p.send(method, *args)
872
+ end
386
873
  rescue Exception => err
387
- #rescue TimeoutError, StandardError, NameError, SyntaxError => err
388
- error "plugin #{p.name} #{method}() failed: #{err.class}: #{err}"
389
- error err.backtrace.join("\n")
874
+ raise if err.kind_of?(SystemExit)
875
+ error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err)
876
+ raise if err.kind_of?(BDB::Fatal)
390
877
  end
391
- end
392
- }
878
+ }
879
+ else
880
+ debug "slow-delegating #{method}"
881
+ @sorted_modules.each { |p|
882
+ if(p.respond_to? method)
883
+ begin
884
+ # debug "#{p.botmodule_class} #{p.name} responds"
885
+ prio = p.priority
886
+ unless (above and above >= prio) or (below and below <= prio)
887
+ ret.push p.send(method, *args)
888
+ end
889
+ rescue Exception => err
890
+ raise if err.kind_of?(SystemExit)
891
+ error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err)
892
+ raise if err.kind_of?(BDB::Fatal)
893
+ end
894
+ end
895
+ }
896
+ end
897
+ return ret
898
+ # debug "Finished delegating #{method.inspect}"
393
899
  end
394
900
 
395
901
  # see if we have a plugin that wants to handle this message, if so, pass
396
902
  # it to the plugin and return true, otherwise false
397
903
  def privmsg(m)
398
- return unless(m.plugin)
399
- if (@@plugins.has_key?(m.plugin) &&
400
- @@plugins[m.plugin].respond_to?("privmsg") &&
401
- @@bot.auth.allow?(m.plugin, m.source, m.replyto))
402
- begin
403
- @@plugins[m.plugin].privmsg(m)
404
- rescue BDB::Fatal => err
405
- error "plugin #{@@plugins[m.plugin].name} privmsg() failed: #{err.class}: #{err}"
406
- error err.backtrace.join("\n")
407
- raise
408
- rescue Exception => err
409
- #rescue TimeoutError, StandardError, NameError, SyntaxError => err
410
- error "plugin #{@@plugins[m.plugin].name} privmsg() failed: #{err.class}: #{err}"
411
- error err.backtrace.join("\n")
904
+ debug "Delegating privmsg #{m.inspect} with pluginkey #{m.plugin.inspect}"
905
+ return unless m.plugin
906
+ k = m.plugin.to_sym
907
+ if commands.has_key?(k)
908
+ p = commands[k][:botmodule]
909
+ a = commands[k][:auth]
910
+ # We check here for things that don't check themselves
911
+ # (e.g. mapped things)
912
+ debug "Checking auth ..."
913
+ if a.nil? || @bot.auth.allow?(a, m.source, m.replyto)
914
+ debug "Checking response ..."
915
+ if p.respond_to?("privmsg")
916
+ begin
917
+ debug "#{p.botmodule_class} #{p.name} responds"
918
+ p.privmsg(m)
919
+ rescue Exception => err
920
+ raise if err.kind_of?(SystemExit)
921
+ error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
922
+ raise if err.kind_of?(BDB::Fatal)
923
+ end
924
+ debug "Successfully delegated #{m.inspect}"
925
+ return true
926
+ else
927
+ debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()"
928
+ end
929
+ else
930
+ debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}"
412
931
  end
413
- return true
932
+ else
933
+ debug "Command #{k} isn't handled"
414
934
  end
415
935
  return false
416
936
  end
937
+
938
+ # delegate IRC messages, by delegating 'listen' first, and the actual method
939
+ # afterwards. Delegating 'privmsg' also delegates ctcp_listen and message
940
+ # as appropriate.
941
+ def irc_delegate(method, m)
942
+ delegate('listen', m)
943
+ if method.to_sym == :privmsg
944
+ delegate('ctcp_listen', m) if m.ctcp
945
+ delegate('message', m)
946
+ privmsg(m) if m.address? and not m.ignored?
947
+ delegate('unreplied', m) unless m.replied
948
+ else
949
+ delegate(method, m)
950
+ end
951
+ end
417
952
  end
418
953
 
954
+ # Returns the only PluginManagerClass instance
955
+ def Plugins.manager
956
+ return PluginManagerClass.instance
957
+ end
958
+
959
+ end
419
960
  end
420
961
  end