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
@@ -1,4 +1,208 @@
1
- require 'rbot/dbhash'
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: Berkeley DB interface
5
+
6
+ begin
7
+ require 'bdb'
8
+ rescue LoadError
9
+ fatal "rbot couldn't load the bdb module, perhaps you need to install it? try: http://www.ruby-lang.org/en/raa-list.rhtml?name=bdb"
10
+ rescue Exception => e
11
+ fatal "rbot couldn't load the bdb module: #{e.pretty_inspect}"
12
+ end
13
+
14
+ if not defined? BDB
15
+ exit 2
16
+ end
17
+
18
+ if BDB::VERSION_MAJOR < 4
19
+ fatal "Your bdb (Berkeley DB) version #{BDB::VERSION} is too old!"
20
+ fatal "rbot will only run with bdb version 4 or higher, please upgrade."
21
+ fatal "For maximum reliability, upgrade to version 4.2 or higher."
22
+ raise BDB::Fatal, BDB::VERSION + " is too old"
23
+ end
24
+
25
+ if BDB::VERSION_MAJOR == 4 and BDB::VERSION_MINOR < 2
26
+ warning "Your bdb (Berkeley DB) version #{BDB::VERSION} may not be reliable."
27
+ warning "If possible, try upgrade version 4.2 or later."
28
+ end
29
+
30
+ # make BTree lookups case insensitive
31
+ module BDB
32
+ class CIBtree < Btree
33
+ def bdb_bt_compare(a, b)
34
+ if a == nil || b == nil
35
+ warning "CIBTree: comparing #{a.inspect} (#{self[a].inspect}) with #{b.inspect} (#{self[b].inspect})"
36
+ end
37
+ (a||'').downcase <=> (b||'').downcase
38
+ end
39
+ end
40
+ end
41
+
42
+ module Irc
43
+
44
+ # DBHash is for tying a hash to disk (using bdb).
45
+ # Call it with an identifier, for example "mydata". It'll look for
46
+ # mydata.db, if it exists, it will load and reference that db.
47
+ # Otherwise it'll create and empty db called mydata.db
48
+ class DBHash
49
+
50
+ # absfilename:: use +key+ as an actual filename, don't prepend the bot's
51
+ # config path and don't append ".db"
52
+ def initialize(bot, key, absfilename=false)
53
+ @bot = bot
54
+ @key = key
55
+ relfilename = @bot.path key
56
+ relfilename << '.db'
57
+ if absfilename && File.exist?(key)
58
+ # db already exists, use it
59
+ @db = DBHash.open_db(key)
60
+ elsif absfilename
61
+ # create empty db
62
+ @db = DBHash.create_db(key)
63
+ elsif File.exist? relfilename
64
+ # db already exists, use it
65
+ @db = DBHash.open_db relfilename
66
+ else
67
+ # create empty db
68
+ @db = DBHash.create_db relfilename
69
+ end
70
+ end
71
+
72
+ def method_missing(method, *args, &block)
73
+ return @db.send(method, *args, &block)
74
+ end
75
+
76
+ def DBHash.create_db(name)
77
+ debug "DBHash: creating empty db #{name}"
78
+ return BDB::Hash.open(name, nil,
79
+ BDB::CREATE | BDB::EXCL, 0600)
80
+ end
81
+
82
+ def DBHash.open_db(name)
83
+ debug "DBHash: opening existing db #{name}"
84
+ return BDB::Hash.open(name, nil, "r+", 0600)
85
+ end
86
+
87
+ end
88
+
89
+
90
+ # DBTree is a BTree equivalent of DBHash, with case insensitive lookups.
91
+ class DBTree
92
+ @@env=nil
93
+ # TODO: make this customizable
94
+ # Note that it must be at least four times lg_bsize
95
+ @@lg_max = 8*1024*1024
96
+ # absfilename:: use +key+ as an actual filename, don't prepend the bot's
97
+ # config path and don't append ".db"
98
+ def initialize(bot, key, absfilename=false)
99
+ @bot = bot
100
+ @key = key
101
+ if @@env.nil?
102
+ begin
103
+ @@env = BDB::Env.open(@bot.botclass, BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER, "set_lg_max" => @@lg_max)
104
+ debug "DBTree: environment opened with max log size #{@@env.conf['lg_max']}"
105
+ rescue => e
106
+ debug "DBTree: failed to open environment: #{e.pretty_inspect}. Retrying ..."
107
+ @@env = BDB::Env.open(@bot.botclass, BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)
108
+ end
109
+ #@@env = BDB::Env.open(@bot.botclass, BDB::CREATE | BDB::INIT_MPOOL | BDB::RECOVER)
110
+ end
111
+
112
+ relfilename = @bot.path key
113
+ relfilename << '.db'
114
+
115
+ if absfilename && File.exist?(key)
116
+ # db already exists, use it
117
+ @db = DBTree.open_db(key)
118
+ elsif absfilename
119
+ # create empty db
120
+ @db = DBTree.create_db(key)
121
+ elsif File.exist? relfilename
122
+ # db already exists, use it
123
+ @db = DBTree.open_db relfilename
124
+ else
125
+ # create empty db
126
+ @db = DBTree.create_db relfilename
127
+ end
128
+ end
129
+
130
+ def method_missing(method, *args, &block)
131
+ return @db.send(method, *args, &block)
132
+ end
133
+
134
+ def DBTree.create_db(name)
135
+ debug "DBTree: creating empty db #{name}"
136
+ return @@env.open_db(BDB::CIBtree, name, nil, BDB::CREATE | BDB::EXCL, 0600)
137
+ end
138
+
139
+ def DBTree.open_db(name)
140
+ debug "DBTree: opening existing db #{name}"
141
+ return @@env.open_db(BDB::CIBtree, name, nil, "r+", 0600)
142
+ end
143
+
144
+ def DBTree.cleanup_logs()
145
+ begin
146
+ debug "DBTree: checkpointing ..."
147
+ @@env.checkpoint
148
+ rescue Exception => e
149
+ debug "Failed: #{e.pretty_inspect}"
150
+ end
151
+ begin
152
+ debug "DBTree: flushing log ..."
153
+ @@env.log_flush
154
+ logs = @@env.log_archive(BDB::ARCH_ABS)
155
+ debug "DBTree: deleting archivable logs: #{logs.join(', ')}."
156
+ logs.each { |log|
157
+ File.delete(log)
158
+ }
159
+ rescue Exception => e
160
+ debug "Failed: #{e.pretty_inspect}"
161
+ end
162
+ end
163
+
164
+ def DBTree.stats()
165
+ begin
166
+ debug "General stats:"
167
+ debug @@env.stat
168
+ debug "Lock stats:"
169
+ debug @@env.lock_stat
170
+ debug "Log stats:"
171
+ debug @@env.log_stat
172
+ debug "Txn stats:"
173
+ debug @@env.txn_stat
174
+ rescue
175
+ debug "Couldn't dump stats"
176
+ end
177
+ end
178
+
179
+ def DBTree.cleanup_env()
180
+ begin
181
+ debug "DBTree: checking transactions ..."
182
+ has_active_txn = @@env.txn_stat["st_nactive"] > 0
183
+ if has_active_txn
184
+ warning "DBTree: not all transactions completed!"
185
+ end
186
+ DBTree.cleanup_logs
187
+ debug "DBTree: closing environment #{@@env}"
188
+ path = @@env.home
189
+ @@env.close
190
+ @@env = nil
191
+ if has_active_txn
192
+ debug "DBTree: keeping file because of incomplete transactions"
193
+ else
194
+ debug "DBTree: cleaning up environment in #{path}"
195
+ BDB::Env.remove("#{path}")
196
+ end
197
+ rescue Exception => e
198
+ error "failed to clean up environment: #{e.pretty_inspect}"
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ end
205
+
2
206
 
3
207
  module Irc
4
208
  class Bot
@@ -16,30 +220,30 @@ class Bot
16
220
  # NB this function is called _early_ in init(), pretty much all you have to
17
221
  # work with is @bot.botclass.
18
222
  def upgrade_data
19
- if File.exist?("#{@bot.botclass}/registry.db")
223
+ oldreg = @bot.path 'registry.db'
224
+ newreg = @bot.path 'plugin_registry.db'
225
+ if File.exist?(oldreg)
20
226
  log _("upgrading old-style (rbot 0.9.5 or earlier) plugin registry to new format")
21
- old = BDB::Hash.open("#{@bot.botclass}/registry.db", nil,
22
- "r+", 0600)
23
- new = BDB::CIBtree.open("#{@bot.botclass}/plugin_registry.db", nil,
24
- BDB::CREATE | BDB::EXCL,
25
- 0600)
227
+ old = BDB::Hash.open(oldreg, nil, "r+", 0600)
228
+ new = BDB::CIBtree.open(newreg, nil, BDB::CREATE | BDB::EXCL, 0600)
26
229
  old.each {|k,v|
27
230
  new[k] = v
28
231
  }
29
232
  old.close
30
233
  new.close
31
- File.rename("#{@bot.botclass}/registry.db", "#{@bot.botclass}/registry.db.old")
234
+ File.rename(oldreg, oldreg + ".old")
32
235
  end
33
236
  end
34
237
 
35
238
  def upgrade_data2
36
- if File.exist?("#{@bot.botclass}/plugin_registry.db")
37
- Dir.mkdir("#{@bot.botclass}/registry") unless File.exist?("#{@bot.botclass}/registry")
38
- env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)# | BDB::TXN_NOSYNC)
239
+ oldreg = @bot.path 'plugin_registry.db'
240
+ newdir = @bot.path 'registry'
241
+ if File.exist?(oldreg)
242
+ Dir.mkdir(newdir) unless File.exist?(newdir)
243
+ env = BDB::Env.open(@bot.botclass, BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)# | BDB::TXN_NOSYNC)
39
244
  dbs = Hash.new
40
245
  log _("upgrading previous (rbot 0.9.9 or earlier) plugin registry to new split format")
41
- old = BDB::CIBtree.open("#{@bot.botclass}/plugin_registry.db", nil,
42
- "r+", 0600, "env" => env)
246
+ old = BDB::CIBtree.open(oldreg, nil, "r+", 0600, "env" => env)
43
247
  old.each {|k,v|
44
248
  prefix,key = k.split("/", 2)
45
249
  prefix.downcase!
@@ -64,7 +268,7 @@ class Bot
64
268
  dbs[prefix][key] = v
65
269
  }
66
270
  old.close
67
- File.rename("#{@bot.botclass}/plugin_registry.db", "#{@bot.botclass}/plugin_registry.db.old")
271
+ File.rename(oldreg, oldreg + ".old")
68
272
  dbs.each {|k,v|
69
273
  log _("closing db #{k}")
70
274
  v.close
@@ -100,7 +304,7 @@ class Bot
100
304
  # blah = @registry[:blah]
101
305
  # blah[:foo] = "flump"
102
306
  # @registry[:blah] = blah
103
-
307
+ #
104
308
  # If you don't need to store objects, and strictly want a persistant hash of
105
309
  # strings, you can override the store/restore methods to suit your needs, for
106
310
  # example (in your plugin):
@@ -126,7 +330,7 @@ class Bot
126
330
  def initialize(bot, name)
127
331
  @bot = bot
128
332
  @name = name.downcase
129
- @filename = "#{@bot.botclass}/registry/#{@name}"
333
+ @filename = @bot.path 'registry', @name
130
334
  dirs = File.dirname(@filename).split("/")
131
335
  dirs.length.times { |i|
132
336
  dir = dirs[0,i+1].join("/")+"/"
@@ -220,25 +424,25 @@ class Bot
220
424
  end
221
425
 
222
426
  # just like Hash#each
223
- def each(&block)
427
+ def each(set=nil, bulk=0, &block)
224
428
  return nil unless File.exist?(@filename)
225
- registry.each {|key,value|
429
+ registry.each(set, bulk) {|key,value|
226
430
  block.call(key, restore(value))
227
431
  }
228
432
  end
229
433
 
230
434
  # just like Hash#each_key
231
- def each_key(&block)
435
+ def each_key(set=nil, bulk=0, &block)
232
436
  return nil unless File.exist?(@filename)
233
- registry.each {|key, value|
437
+ registry.each_key(set, bulk) {|key|
234
438
  block.call(key)
235
439
  }
236
440
  end
237
441
 
238
442
  # just like Hash#each_value
239
- def each_value(&block)
443
+ def each_value(set=nil, bulk=0, &block)
240
444
  return nil unless File.exist?(@filename)
241
- registry.each {|key, value|
445
+ registry.each_value(set, bulk) { |value|
242
446
  block.call(restore(value))
243
447
  }
244
448
  end
@@ -0,0 +1,531 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: DB interface
5
+
6
+ begin
7
+ require 'bdb'
8
+ rescue LoadError
9
+ warning "rbot couldn't load the bdb module. Old registries won't be upgraded"
10
+ rescue Exception => e
11
+ warning "rbot couldn't load the bdb module: #{e.pretty_inspect}"
12
+ end
13
+
14
+
15
+ if BDB::VERSION_MAJOR < 4
16
+ fatal "Your bdb (Berkeley DB) version #{BDB::VERSION} is too old!"
17
+ fatal "rbot will only run with bdb version 4 or higher, please upgrade."
18
+ fatal "For maximum reliability, upgrade to version 4.2 or higher."
19
+ raise BDB::Fatal, BDB::VERSION + " is too old"
20
+ end
21
+
22
+ if BDB::VERSION_MAJOR == 4 and BDB::VERSION_MINOR < 2
23
+ warning "Your bdb (Berkeley DB) version #{BDB::VERSION} may not be reliable."
24
+ warning "If possible, try upgrade version 4.2 or later."
25
+ end
26
+
27
+ require 'tokyocabinet'
28
+
29
+ module Irc
30
+
31
+ if defined? BDB
32
+ # DBHash is for tying a hash to disk (using bdb).
33
+ # Call it with an identifier, for example "mydata". It'll look for
34
+ # mydata.db, if it exists, it will load and reference that db.
35
+ # Otherwise it'll create and empty db called mydata.db
36
+ class DBHash
37
+
38
+ # absfilename:: use +key+ as an actual filename, don't prepend the bot's
39
+ # config path and don't append ".db"
40
+ def initialize(bot, key, absfilename=false)
41
+ @bot = bot
42
+ @key = key
43
+ relfilename = @bot.path key
44
+ relfilename << '.db'
45
+ if absfilename && File.exist?(key)
46
+ # db already exists, use it
47
+ @db = DBHash.open_db(key)
48
+ elsif absfilename
49
+ # create empty db
50
+ @db = DBHash.create_db(key)
51
+ elsif File.exist? relfilename
52
+ # db already exists, use it
53
+ @db = DBHash.open_db relfilename
54
+ else
55
+ # create empty db
56
+ @db = DBHash.create_db relfilename
57
+ end
58
+ end
59
+
60
+ def method_missing(method, *args, &block)
61
+ return @db.send(method, *args, &block)
62
+ end
63
+
64
+ def DBHash.create_db(name)
65
+ debug "DBHash: creating empty db #{name}"
66
+ return BDB::Hash.open(name, nil,
67
+ BDB::CREATE | BDB::EXCL, 0600)
68
+ end
69
+
70
+ def DBHash.open_db(name)
71
+ debug "DBHash: opening existing db #{name}"
72
+ return BDB::Hash.open(name, nil, "r+", 0600)
73
+ end
74
+
75
+ end
76
+ # make BTree lookups case insensitive
77
+ module ::BDB
78
+ class CIBtree < Btree
79
+ def bdb_bt_compare(a, b)
80
+ if a == nil || b == nil
81
+ warning "CIBTree: comparing #{a.inspect} (#{self[a].inspect}) with #{b.inspect} (#{self[b].inspect})"
82
+ end
83
+ (a||'').downcase <=> (b||'').downcase
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ module ::TokyoCabinet
90
+ class CIBDB < TokyoCabinet::BDB
91
+ def open(path, omode)
92
+ res = super
93
+ if res
94
+ self.setcmpfunc(Proc.new do |a, b|
95
+ a.downcase <=> b.downcase
96
+ end)
97
+ end
98
+ res
99
+ end
100
+ end
101
+ end
102
+
103
+ # DBTree is a BTree equivalent of DBHash, with case insensitive lookups.
104
+ class DBTree
105
+ # absfilename:: use +key+ as an actual filename, don't prepend the bot's
106
+ # config path and don't append ".db"
107
+ def initialize(bot, key, absfilename=false)
108
+ @bot = bot
109
+ @key = key
110
+
111
+ relfilename = @bot.path key
112
+ relfilename << '.tdb'
113
+
114
+ if absfilename && File.exist?(key)
115
+ # db already exists, use it
116
+ @db = DBTree.open_db(key)
117
+ elsif absfilename
118
+ # create empty db
119
+ @db = DBTree.create_db(key)
120
+ elsif File.exist? relfilename
121
+ # db already exists, use it
122
+ @db = DBTree.open_db relfilename
123
+ else
124
+ # create empty db
125
+ @db = DBTree.create_db relfilename
126
+ end
127
+ oldbasename = (absfilename ? key : relfilename).gsub(/\.tdb$/, ".db")
128
+ if File.exists? oldbasename and defined? BDB
129
+ # upgrading
130
+ warning "Upgrading old database #{oldbasename}..."
131
+ oldb = ::BDB::Btree.open(oldbasename, nil, "r", 0600)
132
+ oldb.each_key do |k|
133
+ @db.outlist k
134
+ @db.putlist k, (oldb.duplicates(k, false))
135
+ end
136
+ oldb.close
137
+ File.rename oldbasename, oldbasename+".bak"
138
+ end
139
+ @db
140
+ end
141
+
142
+ def method_missing(method, *args, &block)
143
+ return @db.send(method, *args, &block)
144
+ end
145
+
146
+ def DBTree.create_db(name)
147
+ debug "DBTree: creating empty db #{name}"
148
+ db = TokyoCabinet::CIBDB.new
149
+ res = db.open(name, TokyoCabinet::CIBDB::OREADER | TokyoCabinet::CIBDB::OCREAT | TokyoCabinet::CIBDB::OWRITER)
150
+ warning "DBTree: creating empty db #{name}: #{db.errmsg(db.ecode) unless res}"
151
+ return db
152
+ end
153
+
154
+ def DBTree.open_db(name)
155
+ debug "DBTree: opening existing db #{name}"
156
+ db = TokyoCabinet::CIBDB.new
157
+ res = db.open(name, TokyoCabinet::CIBDB::OREADER | TokyoCabinet::CIBDB::OWRITER)
158
+ warning "DBTree:opening db #{name}: #{db.errmsg(db.ecode) unless res}"
159
+ return db
160
+ end
161
+
162
+ def DBTree.cleanup_logs()
163
+ # no-op
164
+ end
165
+
166
+ def DBTree.stats()
167
+ # no-op
168
+ end
169
+
170
+ def DBTree.cleanup_env()
171
+ # no-op
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+
178
+ module Irc
179
+ class Bot
180
+
181
+ # This class is now used purely for upgrading from prior versions of rbot
182
+ # the new registry is split into multiple DBHash objects, one per plugin
183
+ class Registry
184
+ def initialize(bot)
185
+ @bot = bot
186
+ upgrade_data
187
+ upgrade_data2
188
+ end
189
+
190
+ # check for older versions of rbot with data formats that require updating
191
+ # NB this function is called _early_ in init(), pretty much all you have to
192
+ # work with is @bot.botclass.
193
+ def upgrade_data
194
+ if defined? DBHash
195
+ oldreg = @bot.path 'registry.db'
196
+ newreg = @bot.path 'plugin_registry.db'
197
+ if File.exist?(oldreg)
198
+ log _("upgrading old-style (rbot 0.9.5 or earlier) plugin registry to new format")
199
+ old = ::BDB::Hash.open(oldreg, nil, "r+", 0600)
200
+ new = TokyoCabinet::CIBDB.new
201
+ new.open(name, TokyoCabinet::CIBDB::OREADER | TokyoCabinet::CIBDB::OCREAT | TokyoCabinet::CIBDB::OWRITER)
202
+ old.each_key do |k|
203
+ new.outlist k
204
+ new.putlist k, (old.duplicates(k, false))
205
+ end
206
+ old.close
207
+ new.close
208
+ File.rename(oldreg, oldreg + ".old")
209
+ end
210
+ else
211
+ warning "Won't upgrade data: BDB not installed"
212
+ end
213
+ end
214
+
215
+ def upgrade_data2
216
+ oldreg = @bot.path 'plugin_registry.db'
217
+ newdir = @bot.path 'registry'
218
+ if File.exist?(oldreg)
219
+ Dir.mkdir(newdir) unless File.exist?(newdir)
220
+ env = BDB::Env.open(@bot.botclass, BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)# | BDB::TXN_NOSYNC)
221
+ dbs = Hash.new
222
+ log _("upgrading previous (rbot 0.9.9 or earlier) plugin registry to new split format")
223
+ old = BDB::CIBtree.open(oldreg, nil, "r+", 0600, "env" => env)
224
+ old.each {|k,v|
225
+ prefix,key = k.split("/", 2)
226
+ prefix.downcase!
227
+ # subregistries were split with a +, now they are in separate folders
228
+ if prefix.gsub!(/\+/, "/")
229
+ # Ok, this code needs to be put in the db opening routines
230
+ dirs = File.dirname("#{@bot.botclass}/registry/#{prefix}.db").split("/")
231
+ dirs.length.times { |i|
232
+ dir = dirs[0,i+1].join("/")+"/"
233
+ unless File.exist?(dir)
234
+ log _("creating subregistry directory #{dir}")
235
+ Dir.mkdir(dir)
236
+ end
237
+ }
238
+ end
239
+ unless dbs.has_key?(prefix)
240
+ log _("creating db #{@bot.botclass}/registry/#{prefix}.db")
241
+ dbs[prefix] = TokyoCabinet::CIBDB.open("#{@bot.botclass}/registry/#{prefix}.db",
242
+ TokyoCabinet::CIBDB::OREADER | TokyoCabinet::CIBDB::OCREAT | TokyoCabinet::CIBDB::OWRITER)
243
+ end
244
+ dbs[prefix][key] = v
245
+ }
246
+ old.close
247
+ File.rename(oldreg, oldreg + ".old")
248
+ dbs.each {|k,v|
249
+ log _("closing db #{k}")
250
+ v.close
251
+ }
252
+ env.close
253
+ end
254
+ end
255
+
256
+ # This class provides persistent storage for plugins via a hash interface.
257
+ # The default mode is an object store, so you can store ruby objects and
258
+ # reference them with hash keys. This is because the default store/restore
259
+ # methods of the plugins' RegistryAccessor are calls to Marshal.dump and
260
+ # Marshal.restore,
261
+ # for example:
262
+ # blah = Hash.new
263
+ # blah[:foo] = "fum"
264
+ # @registry[:blah] = blah
265
+ # then, even after the bot is shut down and disconnected, on the next run you
266
+ # can access the blah object as it was, with:
267
+ # blah = @registry[:blah]
268
+ # The registry can of course be used to store simple strings, fixnums, etc as
269
+ # well, and should be useful to store or cache plugin data or dynamic plugin
270
+ # configuration.
271
+ #
272
+ # WARNING:
273
+ # in object store mode, don't make the mistake of treating it like a live
274
+ # object, e.g. (using the example above)
275
+ # @registry[:blah][:foo] = "flump"
276
+ # will NOT modify the object in the registry - remember that Registry#[]
277
+ # returns a Marshal.restore'd object, the object you just modified in place
278
+ # will disappear. You would need to:
279
+ # blah = @registry[:blah]
280
+ # blah[:foo] = "flump"
281
+ # @registry[:blah] = blah
282
+ #
283
+ # If you don't need to store objects, and strictly want a persistant hash of
284
+ # strings, you can override the store/restore methods to suit your needs, for
285
+ # example (in your plugin):
286
+ # def initialize
287
+ # class << @registry
288
+ # def store(val)
289
+ # val
290
+ # end
291
+ # def restore(val)
292
+ # val
293
+ # end
294
+ # end
295
+ # end
296
+ # Your plugins section of the registry is private, it has its own namespace
297
+ # (derived from the plugin's class name, so change it and lose your data).
298
+ # Calls to registry.each etc, will only iterate over your namespace.
299
+ class Accessor
300
+
301
+ attr_accessor :recovery
302
+
303
+ # plugins don't call this - a Registry::Accessor is created for them and
304
+ # is accessible via @registry.
305
+ def initialize(bot, name)
306
+ @bot = bot
307
+ @name = name.downcase
308
+ @filename = @bot.path 'registry', @name
309
+ dirs = File.dirname(@filename).split("/")
310
+ dirs.length.times { |i|
311
+ dir = dirs[0,i+1].join("/")+"/"
312
+ unless File.exist?(dir)
313
+ debug "creating subregistry directory #{dir}"
314
+ Dir.mkdir(dir)
315
+ end
316
+ }
317
+ @filename << ".tdb"
318
+ @registry = nil
319
+ @default = nil
320
+ @recovery = nil
321
+ # debug "initializing registry accessor with name #{@name}"
322
+ end
323
+
324
+ def registry
325
+ @registry ||= DBTree.new @bot, "registry/#{@name}"
326
+ end
327
+
328
+ def flush
329
+ # debug "fushing registry #{registry}"
330
+ return if !@registry
331
+ registry.sync
332
+ end
333
+
334
+ def close
335
+ # debug "closing registry #{registry}"
336
+ return if !@registry
337
+ registry.close
338
+ end
339
+
340
+ # convert value to string form for storing in the registry
341
+ # defaults to Marshal.dump(val) but you can override this in your module's
342
+ # registry object to use any method you like.
343
+ # For example, if you always just handle strings use:
344
+ # def store(val)
345
+ # val
346
+ # end
347
+ def store(val)
348
+ Marshal.dump(val)
349
+ end
350
+
351
+ # restores object from string form, restore(store(val)) must return val.
352
+ # If you override store, you should override restore to reverse the
353
+ # action.
354
+ # For example, if you always just handle strings use:
355
+ # def restore(val)
356
+ # val
357
+ # end
358
+ def restore(val)
359
+ begin
360
+ Marshal.restore(val)
361
+ rescue Exception => e
362
+ error _("failed to restore marshal data for #{val.inspect}, attempting recovery or fallback to default")
363
+ debug e
364
+ if defined? @recovery and @recovery
365
+ begin
366
+ return @recovery.call(val)
367
+ rescue Exception => ee
368
+ error _("marshal recovery failed, trying default")
369
+ debug ee
370
+ end
371
+ end
372
+ return default
373
+ end
374
+ end
375
+
376
+ # lookup a key in the registry
377
+ def [](key)
378
+ if File.exist?(@filename) and registry.has_key?(key.to_s)
379
+ return restore(registry[key.to_s])
380
+ else
381
+ return default
382
+ end
383
+ end
384
+
385
+ # set a key in the registry
386
+ def []=(key,value)
387
+ registry[key.to_s] = store(value)
388
+ end
389
+
390
+ # set the default value for registry lookups, if the key sought is not
391
+ # found, the default will be returned. The default default (har) is nil.
392
+ def set_default (default)
393
+ @default = default
394
+ end
395
+
396
+ def default
397
+ @default && (@default.dup rescue @default)
398
+ end
399
+
400
+ # just like Hash#each
401
+ def each(set=nil, bulk=0, &block)
402
+ return nil unless File.exist?(@filename)
403
+ registry.fwmkeys(set).each {|key|
404
+ block.call(key, restore(registry[key]))
405
+ }
406
+ end
407
+
408
+ # just like Hash#each_key
409
+ def each_key(set=nil, bulk=0, &block)
410
+ return nil unless File.exist?(@filename)
411
+ registry.fwmkeys(set).each do |key|
412
+ block.call(key)
413
+ end
414
+ end
415
+
416
+ # just like Hash#each_value
417
+ def each_value(set=nil, bulk=0, &block)
418
+ return nil unless File.exist?(@filename)
419
+ registry.fwmkeys(set).each do |key|
420
+ block.call(restore(registry[key]))
421
+ end
422
+ end
423
+
424
+ # just like Hash#has_key?
425
+ def has_key?(key)
426
+ return false unless File.exist?(@filename)
427
+ return registry.has_key?(key.to_s)
428
+ end
429
+
430
+ alias include? has_key?
431
+ alias member? has_key?
432
+ alias key? has_key?
433
+
434
+ # just like Hash#has_both?
435
+ def has_both?(key, value)
436
+ return false unless File.exist?(@filename)
437
+ registry.has_key?(key.to_s) and registry.has_value?(store(value))
438
+ end
439
+
440
+ # just like Hash#has_value?
441
+ def has_value?(value)
442
+ return false unless File.exist?(@filename)
443
+ return registry.has_value?(store(value))
444
+ end
445
+
446
+ # just like Hash#index?
447
+ def index(value)
448
+ self.each do |k,v|
449
+ return k if v == value
450
+ end
451
+ return nil
452
+ end
453
+
454
+ # delete a key from the registry
455
+ def delete(key)
456
+ return default unless File.exist?(@filename)
457
+ return registry.delete(key.to_s)
458
+ end
459
+
460
+ # returns a list of your keys
461
+ def keys
462
+ return [] unless File.exist?(@filename)
463
+ return registry.keys
464
+ end
465
+
466
+ # Return an array of all associations [key, value] in your namespace
467
+ def to_a
468
+ return [] unless File.exist?(@filename)
469
+ ret = Array.new
470
+ registry.each {|key, value|
471
+ ret << [key, restore(value)]
472
+ }
473
+ return ret
474
+ end
475
+
476
+ # Return an hash of all associations {key => value} in your namespace
477
+ def to_hash
478
+ return {} unless File.exist?(@filename)
479
+ ret = Hash.new
480
+ registry.each {|key, value|
481
+ ret[key] = restore(value)
482
+ }
483
+ return ret
484
+ end
485
+
486
+ # empties the registry (restricted to your namespace)
487
+ def clear
488
+ return true unless File.exist?(@filename)
489
+ registry.vanish
490
+ end
491
+ alias truncate clear
492
+
493
+ # returns an array of the values in your namespace of the registry
494
+ def values
495
+ return [] unless File.exist?(@filename)
496
+ ret = Array.new
497
+ self.each {|k,v|
498
+ ret << restore(v)
499
+ }
500
+ return ret
501
+ end
502
+
503
+ def sub_registry(prefix)
504
+ return Accessor.new(@bot, @name + "/" + prefix.to_s)
505
+ end
506
+
507
+ # returns the number of keys in your registry namespace
508
+ def length
509
+ return 0 unless File.exist?(@filename)
510
+ registry.length
511
+ end
512
+ alias size length
513
+
514
+ # That is btree!
515
+ def putdup(key, value)
516
+ registry.putdup(key.to_s, store(value))
517
+ end
518
+
519
+ def putlist(key, values)
520
+ registry.putlist(key.to_s, value.map {|v| store(v)})
521
+ end
522
+
523
+ def getlist(key)
524
+ return [] unless File.exist?(@filename)
525
+ (registry.getlist(key.to_s) || []).map {|v| restore(v)}
526
+ end
527
+ end
528
+
529
+ end
530
+ end
531
+ end