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
@@ -23,7 +23,7 @@ $log_thread = nil
23
23
 
24
24
  require 'pp'
25
25
 
26
- unless Kernel.instance_methods.include?("pretty_inspect")
26
+ unless Kernel.respond_to? :pretty_inspect
27
27
  def pretty_inspect
28
28
  PP.pp(self, '')
29
29
  end
@@ -41,6 +41,9 @@ class Exception
41
41
  end
42
42
  end
43
43
 
44
+ class ServerError < RuntimeError
45
+ end
46
+
44
47
  def rawlog(level, message=nil, who_pos=1)
45
48
  call_stack = caller
46
49
  if call_stack.length > who_pos
@@ -132,6 +135,12 @@ $interrupted = 0
132
135
 
133
136
  # these first
134
137
  require 'rbot/rbotconfig'
138
+ begin
139
+ require 'rubygems'
140
+ rescue LoadError
141
+ log "rubygems unavailable"
142
+ end
143
+
135
144
  require 'rbot/load-gettext'
136
145
  require 'rbot/config'
137
146
  require 'rbot/config-compat'
@@ -144,8 +153,6 @@ require 'rbot/timer'
144
153
  require 'rbot/plugins'
145
154
  require 'rbot/message'
146
155
  require 'rbot/language'
147
- require 'rbot/dbhash'
148
- require 'rbot/registry'
149
156
 
150
157
  module Irc
151
158
 
@@ -202,11 +209,16 @@ class Bot
202
209
  @client.user
203
210
  end
204
211
 
205
- # bot User in the client/server connection
212
+ # bot nick in the client/server connection
206
213
  def nick
207
214
  myself.nick
208
215
  end
209
216
 
217
+ # bot channels in the client/server connection
218
+ def channels
219
+ myself.channels
220
+ end
221
+
210
222
  # nick wanted by the bot. This defaults to the irc.nick config value,
211
223
  # but may be overridden by a manual !nick command
212
224
  def wanted_nick
@@ -303,6 +315,9 @@ class Bot
303
315
  Config.register Config::ArrayValue.new('irc.ignore_users',
304
316
  :default => [],
305
317
  :desc => "Which users to ignore input from. This is mainly to avoid bot-wars triggered by creative people")
318
+ Config.register Config::ArrayValue.new('irc.ignore_channels',
319
+ :default => [],
320
+ :desc => "Which channels to ignore input in. This is mainly to turn the bot into a logbot that doesn't interact with users in any way (in the specified channels)")
306
321
 
307
322
  Config.register Config::IntegerValue.new('core.save_every',
308
323
  :default => 60, :validate => Proc.new{|v| v >= 0},
@@ -395,6 +410,19 @@ class Bot
395
410
  bot.set_default_send_options :truncate_text => v.dup
396
411
  },
397
412
  :desc => "When truncating overlong messages (see send.overlong) or when sending too many lines per message (see send.max_lines) replace the end of the last line with this text")
413
+ Config.register Config::IntegerValue.new('send.penalty_pct',
414
+ :default => 100,
415
+ :validate => Proc.new { |v| v >= 0 },
416
+ :on_change => Proc.new { |bot, v|
417
+ bot.socket.penalty_pct = v
418
+ },
419
+ :desc => "Percentage of IRC penalty to consider when sending messages to prevent being disconnected for excess flood. Set to 0 to disable penalty control.")
420
+ Config.register Config::StringValue.new('core.db',
421
+ :default => "bdb",
422
+ :wizard => true, :default => "bdb",
423
+ :validate => Proc.new { |v| ["bdb", "tc"].include? v },
424
+ :requires_restart => true,
425
+ :desc => "DB adaptor to use for storing settings and plugin data. Options are: bdb (Berkeley DB, stable adaptor, but troublesome to install and unmaintained), tc (Tokyo Cabinet, new adaptor, fast and furious, but may be not available and contain bugs)")
398
426
 
399
427
  @argv = params[:argv]
400
428
  @run_dir = params[:run_dir] || Dir.pwd
@@ -424,36 +452,25 @@ class Bot
424
452
  botclass.gsub!("\\","/")
425
453
  end
426
454
  end
427
- botclass += "/.rbot"
455
+ botclass = File.join(botclass, ".rbot")
428
456
  end
429
457
  botclass = File.expand_path(botclass)
430
458
  @botclass = botclass.gsub(/\/$/, "")
431
459
 
432
- if FileTest.directory? botclass
433
- # compare the templates dir with the current botclass, and fill it in with
434
- # any missing file.
435
- # Sadly, FileUtils.cp_r doesn't have an :update option, so we have to do it
436
- # manually
437
- template = File.join Config::datadir, 'templates'
438
- # note that we use the */** pattern because we don't want to match
439
- # keywords.rbot, which gets deleted on load and would therefore be missing always
440
- missing = Dir.chdir(template) { Dir.glob('*/**') } - Dir.chdir(botclass) { Dir.glob('*/**') }
441
- missing.map do |f|
442
- dest = File.join(botclass, f)
443
- FileUtils.mkdir_p File.dirname dest
444
- FileUtils.cp File.join(template, f), dest
445
- end
446
- else
447
- log "no #{botclass} directory found, creating from templates.."
448
- if FileTest.exist? botclass
449
- error "file #{botclass} exists but isn't a directory"
450
- exit 2
451
- end
452
- FileUtils.cp_r Config::datadir+'/templates', botclass
453
- end
460
+ repopulate_botclass_directory
454
461
 
455
- Dir.mkdir("#{botclass}/registry") unless File.exist?("#{botclass}/registry")
456
- Dir.mkdir("#{botclass}/safe_save") unless File.exist?("#{botclass}/safe_save")
462
+ registry_dir = File.join(@botclass, 'registry')
463
+ Dir.mkdir(registry_dir) unless File.exist?(registry_dir)
464
+ unless FileTest.directory? registry_dir
465
+ error "registry storage location #{registry_dir} is not a directory"
466
+ exit 2
467
+ end
468
+ save_dir = File.join(@botclass, 'safe_save')
469
+ Dir.mkdir(save_dir) unless File.exist?(save_dir)
470
+ unless FileTest.directory? save_dir
471
+ error "safe save location #{save_dir} is not a directory"
472
+ exit 2
473
+ end
457
474
 
458
475
  # Time at which the last PING was sent
459
476
  @last_ping = nil
@@ -474,10 +491,20 @@ class Bot
474
491
  if @config['core.run_as_daemon']
475
492
  $daemonize = true
476
493
  end
494
+ case @config["core.db"]
495
+ when "bdb"
496
+ require 'rbot/registry/bdb'
497
+ when "tc"
498
+ require 'rbot/registry/tc'
499
+ else
500
+ raise _("Unknown DB adaptor: %s") % @config["core.db"]
501
+ end
477
502
 
478
503
  @logfile = @config['log.file']
479
504
  if @logfile.class!=String || @logfile.empty?
480
- @logfile = "#{botclass}/#{File.basename(botclass).gsub(/^\.+/,'')}.log"
505
+ logfname = File.basename(botclass).gsub(/^\.+/,'')
506
+ logfname << ".log"
507
+ @logfile = File.join(botclass, logfname)
481
508
  debug "Using `#{@logfile}' as debug log"
482
509
  end
483
510
 
@@ -536,7 +563,7 @@ class Bot
536
563
  end
537
564
  end
538
565
 
539
- File.open($opts['pidfile'] || "#{@botclass}/rbot.pid", 'w') do |pf|
566
+ File.open($opts['pidfile'] || File.join(@botclass, 'rbot.pid'), 'w') do |pf|
540
567
  pf << "#{$$}\n"
541
568
  end
542
569
 
@@ -566,7 +593,6 @@ class Bot
566
593
  @auth.everyone.set_default_permission("*", true)
567
594
  @auth.botowner.password= @config['auth.password']
568
595
 
569
- Dir.mkdir("#{botclass}/plugins") unless File.exist?("#{botclass}/plugins")
570
596
  @plugins = Plugins::manager
571
597
  @plugins.bot_associate(self)
572
598
  setup_plugins_path()
@@ -581,7 +607,7 @@ class Bot
581
607
  debug "server.list is now #{@config['server.list'].inspect}"
582
608
  end
583
609
 
584
- @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], :ssl => @config['server.ssl'])
610
+ @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], :ssl => @config['server.ssl'], :penalty_pct =>@config['send.penalty_pct'])
585
611
  @client = Client.new
586
612
 
587
613
  @plugins.scan
@@ -629,11 +655,18 @@ class Bot
629
655
  # debug "Message target is #{data[:target].inspect}"
630
656
  # debug "Bot is #{myself.inspect}"
631
657
 
658
+ @config['irc.ignore_channels'].each { |channel|
659
+ if m.target.downcase == channel.downcase
660
+ m.ignored = true
661
+ break
662
+ end
663
+ }
632
664
  @config['irc.ignore_users'].each { |mask|
633
665
  if m.source.matches?(server.new_netmask(mask))
634
666
  m.ignored = true
667
+ break
635
668
  end
636
- }
669
+ } unless m.ignored
637
670
 
638
671
  @plugins.irc_delegate('privmsg', m)
639
672
  }
@@ -736,6 +769,18 @@ class Bot
736
769
  m.users = data[:users]
737
770
  @plugins.delegate "names", m
738
771
  }
772
+ @client[:banlist] = proc { |data|
773
+ m = BanlistMessage.new(self, server, server, data[:channel])
774
+ m.bans = data[:bans]
775
+ @plugins.delegate "banlist", m
776
+ }
777
+ @client[:nosuchtarget] = proc { |data|
778
+ m = NoSuchTargetMessage.new(self, server, server, data[:target], data[:message])
779
+ @plugins.delegate "nosuchtarget", m
780
+ }
781
+ @client[:error] = proc { |data|
782
+ raise ServerError, data[:message]
783
+ }
739
784
  @client[:unknown] = proc { |data|
740
785
  #debug "UNKNOWN: #{data[:serverstring]}"
741
786
  m = UnknownMessage.new(self, server, server, nil, data[:serverstring])
@@ -753,15 +798,54 @@ class Bot
753
798
  trap_sigs
754
799
  end
755
800
 
801
+ def repopulate_botclass_directory
802
+ template_dir = File.join Config::datadir, 'templates'
803
+ if FileTest.directory? @botclass
804
+ # compare the templates dir with the current botclass dir, filling up the
805
+ # latter with any missing file. Sadly, FileUtils.cp_r doesn't have an
806
+ # :update option, so we have to do it manually.
807
+ # Note that we use the */** pattern because we don't want to match
808
+ # keywords.rbot, which gets deleted on load and would therefore be missing
809
+ # always
810
+ missing = Dir.chdir(template_dir) { Dir.glob('*/**') } - Dir.chdir(@botclass) { Dir.glob('*/**') }
811
+ missing.map do |f|
812
+ dest = File.join(@botclass, f)
813
+ FileUtils.mkdir_p(File.dirname(dest))
814
+ FileUtils.cp File.join(template_dir, f), dest
815
+ end
816
+ else
817
+ log "no #{@botclass} directory found, creating from templates..."
818
+ if FileTest.exist? @botclass
819
+ error "file #{@botclass} exists but isn't a directory"
820
+ exit 2
821
+ end
822
+ FileUtils.cp_r template_dir, @botclass
823
+ end
824
+ end
825
+
826
+ # Return a path under the current botclass by joining the mentioned
827
+ # components. The components are automatically converted to String
828
+ def path(*components)
829
+ File.join(@botclass, *(components.map {|c| c.to_s}))
830
+ end
831
+
756
832
  def setup_plugins_path
833
+ plugdir_default = File.join(Config::datadir, 'plugins')
834
+ plugdir_local = File.join(@botclass, 'plugins')
835
+ Dir.mkdir(plugdir_local) unless File.exist?(plugdir_local)
836
+
757
837
  @plugins.clear_botmodule_dirs
758
- @plugins.add_botmodule_dir(Config::coredir + "/utils")
759
- @plugins.add_botmodule_dir(Config::coredir)
760
- @plugins.add_botmodule_dir("#{botclass}/plugins")
838
+ @plugins.add_core_module_dir(File.join(Config::coredir, 'utils'))
839
+ @plugins.add_core_module_dir(Config::coredir)
840
+ if FileTest.directory? plugdir_local
841
+ @plugins.add_plugin_dir(plugdir_local)
842
+ else
843
+ warning "local plugin location #{plugdir_local} is not a directory"
844
+ end
761
845
 
762
846
  @config['plugins.path'].each do |_|
763
- path = _.sub(/^\(default\)/, Config::datadir + '/plugins')
764
- @plugins.add_botmodule_dir(path)
847
+ path = _.sub(/^\(default\)/, plugdir_default)
848
+ @plugins.add_plugin_dir(path)
765
849
  end
766
850
  end
767
851
 
@@ -782,7 +866,7 @@ class Bot
782
866
  }
783
867
  end
784
868
  @default_send_options.update opts unless opts.empty?
785
- end
869
+ end
786
870
 
787
871
  # checks if we should be quiet on a channel
788
872
  def quiet_on?(channel)
@@ -816,7 +900,8 @@ class Bot
816
900
  # things to do when we receive a signal
817
901
  def got_sig(sig, func=:quit)
818
902
  debug "received #{sig}, queueing #{func}"
819
- $interrupted += 1
903
+ # this is not an interruption if we just need to reconnect
904
+ $interrupted += 1 unless func == :reconnect
820
905
  self.send(func) unless @quit_mutex.locked?
821
906
  debug "interrupted #{$interrupted} times"
822
907
  if $interrupted >= 3
@@ -832,6 +917,7 @@ class Bot
832
917
  trap("SIGINT") { got_sig("SIGINT") }
833
918
  trap("SIGTERM") { got_sig("SIGTERM") }
834
919
  trap("SIGHUP") { got_sig("SIGHUP", :restart) }
920
+ trap("SIGUSR1") { got_sig("SIGUSR1", :reconnect) }
835
921
  rescue ArgumentError => e
836
922
  debug "failed to trap signals (#{e.pretty_inspect}): running on Windows?"
837
923
  rescue Exception => e
@@ -841,11 +927,15 @@ class Bot
841
927
 
842
928
  # connect the bot to IRC
843
929
  def connect
930
+ # make sure we don't have any spurious ping checks running
931
+ # (and initialize the vars if this is the first time we connect)
932
+ stop_server_pings
844
933
  begin
845
934
  quit if $interrupted > 0
846
935
  @socket.connect
936
+ @last_rec = Time.now
847
937
  rescue => e
848
- raise e.class, "failed to connect to IRC server at #{@socket.server_uri}: " + e
938
+ raise e.class, "failed to connect to IRC server at #{@socket.server_uri}: #{e}"
849
939
  end
850
940
  quit if $interrupted > 0
851
941
 
@@ -859,14 +949,44 @@ class Bot
859
949
  myself.user = @config['irc.user']
860
950
  end
861
951
 
952
+ # disconnect the bot from IRC, if connected, and then connect (again)
953
+ def reconnect(message=nil, too_fast=false)
954
+ # we will wait only if @last_rec was not nil, i.e. if we were connected or
955
+ # got disconnected by a network error
956
+ # if someone wants to manually call disconnect() _and_ reconnect(), they
957
+ # will have to take care of the waiting themselves
958
+ will_wait = !!@last_rec
959
+
960
+ if @socket.connected?
961
+ disconnect(message)
962
+ end
963
+
964
+ begin
965
+ if will_wait
966
+ log "\n\nDisconnected\n\n"
967
+
968
+ quit if $interrupted > 0
969
+
970
+ log "\n\nWaiting to reconnect\n\n"
971
+ sleep @config['server.reconnect_wait']
972
+ sleep 10*@config['server.reconnect_wait'] if too_fast
973
+ end
974
+
975
+ connect
976
+ rescue Exception => e
977
+ will_wait = true
978
+ retry
979
+ end
980
+ end
981
+
862
982
  # begin event handling loop
863
983
  def mainloop
864
984
  while true
985
+ too_fast = false
865
986
  begin
866
- quit if $interrupted > 0
867
- connect
868
-
869
987
  quit_msg = nil
988
+ reconnect(quit_msg, too_fast)
989
+ quit if $interrupted > 0
870
990
  while @socket.connected?
871
991
  quit if $interrupted > 0
872
992
 
@@ -892,6 +1012,11 @@ class Bot
892
1012
  rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e
893
1013
  error "network exception: #{e.pretty_inspect}"
894
1014
  quit_msg = e.to_s
1015
+ rescue ServerError => e
1016
+ # received an ERROR from the server
1017
+ quit_msg = "server ERROR: " + e.message
1018
+ too_fast = e.message.index("reconnect too fast")
1019
+ retry
895
1020
  rescue BDB::Fatal => e
896
1021
  fatal "fatal bdb error: #{e.pretty_inspect}"
897
1022
  DBTree.stats
@@ -907,15 +1032,6 @@ class Bot
907
1032
  log_session_end
908
1033
  exit 2
909
1034
  end
910
-
911
- disconnect(quit_msg)
912
-
913
- log "\n\nDisconnected\n\n"
914
-
915
- quit if $interrupted > 0
916
-
917
- log "\n\nWaiting to reconnect\n\n"
918
- sleep @config['server.reconnect_wait']
919
1035
  end
920
1036
  end
921
1037
 
@@ -926,8 +1042,22 @@ class Bot
926
1042
  # Type can be PRIVMSG, NOTICE, etc, but those you should really use the
927
1043
  # relevant say() or notice() methods. This one should be used for IRCd
928
1044
  # extensions you want to use in modules.
929
- def sendmsg(type, where, original_message, options={})
930
- opts = @default_send_options.merge(options)
1045
+ def sendmsg(original_type, original_where, original_message, options={})
1046
+
1047
+ # filter message with sendmsg filters
1048
+ ds = DataStream.new original_message.to_s.dup,
1049
+ :type => original_type, :dest => original_where,
1050
+ :options => @default_send_options.merge(options)
1051
+ filters = filter_names(:sendmsg)
1052
+ filters.each do |fname|
1053
+ debug "filtering #{ds[:text]} with sendmsg filter #{fname}"
1054
+ ds.merge! filter(self.global_filter_name(fname, :sendmsg), ds)
1055
+ end
1056
+
1057
+ opts = ds[:options]
1058
+ type = ds[:type]
1059
+ where = ds[:dest]
1060
+ filtered = ds[:text]
931
1061
 
932
1062
  # For starters, set up appropriate queue channels and rings
933
1063
  mchan = opts[:queue_channel]
@@ -948,7 +1078,7 @@ class Bot
948
1078
  end
949
1079
  end
950
1080
 
951
- multi_line = original_message.to_s.gsub(/[\r\n]+/, "\n")
1081
+ multi_line = filtered.gsub(/[\r\n]+/, "\n")
952
1082
 
953
1083
  # if target is a channel with nocolor modes, strip colours
954
1084
  if where.kind_of?(Channel) and where.mode.any?(*config['server.nocolor_modes'])
@@ -1151,7 +1281,9 @@ class Bot
1151
1281
 
1152
1282
  # totally shutdown and respawn the bot
1153
1283
  def restart(message=nil)
1154
- message = "restarting, back in #{@config['server.reconnect_wait']}..." if (!message || message.empty?)
1284
+ message = _("restarting, back in %{wait}...") % {
1285
+ :wait => @config['server.reconnect_wait']
1286
+ } if (!message || message.empty?)
1155
1287
  shutdown(message)
1156
1288
  sleep @config['server.reconnect_wait']
1157
1289
  begin
@@ -48,8 +48,14 @@ class ::String
48
48
  dests = pars.split($;,2).first
49
49
  penalty += dests.split(',').size
50
50
  when :WHO
51
- # I'm too lazy to implement this one correctly
52
- penalty += 5
51
+ args = pars.split
52
+ if args.length > 0
53
+ penalty += args.inject(0){ |sum,x| sum += ((x.length > 4) ? 3 : 5) }
54
+ else
55
+ penalty += 10
56
+ end
57
+ when :PART
58
+ penalty += 4
53
59
  when :AWAY, :JOIN, :VERSION, :TIME, :TRACE, :WHOIS, :DNS
54
60
  penalty += 2
55
61
  when :INVITE, :NICK
@@ -246,6 +252,9 @@ module Irc
246
252
  # normalized uri of the current server
247
253
  attr_reader :server_uri
248
254
 
255
+ # penalty multiplier (percent)
256
+ attr_accessor :penalty_pct
257
+
249
258
  # default trivial filter class
250
259
  class IdentityFilter
251
260
  def in(x)
@@ -275,11 +284,8 @@ module Irc
275
284
  @spooler = false
276
285
  @lines_sent = 0
277
286
  @lines_received = 0
278
- if opts.kind_of?(Hash) and opts.key?(:ssl)
279
- @ssl = opts[:ssl]
280
- else
281
- @ssl = false
282
- end
287
+ @ssl = opts[:ssl]
288
+ @penalty_pct = opts[:penalty_pct] || 100
283
289
  end
284
290
 
285
291
  def connected?
@@ -437,7 +443,7 @@ module Irc
437
443
  @sock.syswrite actual
438
444
  @last_send = now
439
445
  @flood_send = now if @flood_send < now
440
- @flood_send += message.irc_send_penalty if penalty
446
+ @flood_send += message.irc_send_penalty*@penalty_pct/100.0 if penalty
441
447
  @lines_sent += 1
442
448
  end
443
449
  rescue Exception => e