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
@@ -45,13 +45,13 @@ class TimePlugin < Plugin
45
45
  end
46
46
  ['/', '_'].each { |sp|
47
47
  arr = Array.new
48
- zone.split(sp).each{ |s|
48
+ zone.split(sp).each{ |s|
49
49
  s[0] = s[0,1].upcase
50
50
  s[1, s.length] = s[1, s.length].downcase if sp == '/'
51
51
  arr.push(s) }
52
52
  zone = arr.join( sp )
53
53
  }
54
-
54
+
55
55
  tz = TZInfo::Timezone.get( zone )
56
56
  "#{tz.friendly_identifier} - #{tz.now.strftime( '%a %b %d %H:%M' )} #{tz.current_period.abbreviation}"
57
57
  end
@@ -39,7 +39,7 @@ class Translator
39
39
  @cache = cache
40
40
  end
41
41
 
42
-
42
+
43
43
  # whether the translator supports this direction
44
44
  def support?(from, to)
45
45
  from != to && @directions[from].include?(to)
@@ -110,17 +110,19 @@ class NiftyTranslator < Translator
110
110
  def initialize(cache={})
111
111
  require 'mechanize'
112
112
  super(Translator::Direction.all_from_to(%w[ja en zh_CN ko], %w[ja]), cache)
113
- @form = WWW::Mechanize.new.
114
- get('http://nifty.amikai.com/amitext/indexUTF8.jsp').
115
- forms.name('translateForm').first
116
113
  end
117
114
 
118
115
  def do_translate(text, from, to)
119
- @form.radiobuttons.name('langpair').value = "#{from},#{to}".upcase
120
- @form.fields.name('sourceText').value = text
121
-
122
- @form.submit(@form.buttons.name('translate')).
123
- forms.name('translateForm').fields.name('translatedText').value
116
+ @form ||= WWW::Mechanize.new.
117
+ get('http://nifty.amikai.com/amitext/indexUTF8.jsp').
118
+ forms_with(:name => 'translateForm').last
119
+ @radio = @form.radiobuttons_with(:name => 'langpair').first
120
+ @radio.value = "#{from},#{to}".upcase
121
+ @radio.check
122
+ @form.fields_with(:name => 'sourceText').last.value = text
123
+
124
+ @form.submit(@form.buttons_with(:name => 'translate').last).
125
+ forms_with(:name => 'translateForm').last.fields_with(:name => 'translatedText').last.value
124
126
  end
125
127
  end
126
128
 
@@ -149,7 +151,7 @@ class ExciteTranslator < Translator
149
151
 
150
152
  def open_form(name)
151
153
  WWW::Mechanize.new.get("http://www.excite.co.jp/world/#{name}").
152
- forms.name('world').first
154
+ forms_with(:name => 'world').first
153
155
  end
154
156
 
155
157
  def do_translate(text, from, to)
@@ -157,15 +159,15 @@ class ExciteTranslator < Translator
157
159
  form = @forms[non_ja_language]
158
160
 
159
161
  if non_ja_language =~ /zh_(CN|TW)/
160
- form.fields.name('wb_lp').value = "#{from}#{to}".sub(/_(?:CN|TW)/, '').upcase
161
- form.fields.name('big5').value = ($1 == 'TW' ? 'yes' : 'no')
162
+ form_with_fields(:name => 'wb_lp').first.value = "#{from}#{to}".sub(/_(?:CN|TW)/, '').upcase
163
+ form_with_fields(:name => 'big5').first.value = ($1 == 'TW' ? 'yes' : 'no')
162
164
  else
163
165
  # the en<->ja page is in Shift_JIS while other pages are UTF-8
164
166
  text = Iconv.iconv('Shift_JIS', 'UTF-8', text) if non_ja_language == 'en'
165
- form.fields.name('wb_lp').value = "#{from}#{to}".upcase
167
+ form.fields_with(:name => 'wb_lp').first.value = "#{from}#{to}".upcase
166
168
  end
167
- form.fields.name('before').value = text
168
- result = form.submit.forms.name('world').fields.name('after').value
169
+ form.fields_with(:name => 'before').first.value = text
170
+ result = form.submit.forms_with(:name => 'world').first.fields_with(:name => 'after').first.value
169
171
  # the en<->ja page is in Shift_JIS while other pages are UTF-8
170
172
  if non_ja_language == 'en'
171
173
  Iconv.iconv('UTF-8', 'Shift_JIS', result)
@@ -180,31 +182,30 @@ end
180
182
  class GoogleTranslator < Translator
181
183
  INFO = 'Google Translate <http://www.google.com/translate_t>'
182
184
 
185
+ LANGUAGES =
186
+ %w[af sq am ar hy az eu be bn bh bg my ca chr zh zh_CN zh_TW hr
187
+ cs da dv en eo et tl fi fr gl ka de el gn gu iw hi hu is id iu
188
+ ga it ja kn kk km ko lv lt mk ms ml mt mr mn ne no or ps fa pl
189
+ pt_PT pa ro ru sa sr sd si sk sl es sw sv tg ta tl te th bo tr
190
+ uk ur uz ug vi cy yi auto]
183
191
  def initialize(cache={})
184
- require 'mechanize'
185
- load_form!
186
- language_pairs = @lang_list.options.map do |o|
187
- # these options have values like "en|zh-CN"; map to things like ['en', 'zh_CN'].
188
- o.value.split('|').map {|l| l.sub('-', '_')}
189
- end
190
- super(Translator::Direction.pairs(language_pairs), cache)
191
- end
192
-
193
- def load_form!
194
- agent = WWW::Mechanize.new
195
- # without faking the user agent, Google Translate will serve non-UTF-8 text
196
- agent.user_agent_alias = 'Linux Konqueror'
197
- @form = agent.get('http://www.google.com/translate_t').
198
- forms.action('/translate_t').first
199
- @lang_list = @form.fields.name('langpair')
192
+ require "uri"
193
+ require "json"
194
+ super(Translator::Direction.all_to_all(LANGUAGES), cache)
200
195
  end
201
196
 
202
197
  def do_translate(text, from, to)
203
- load_form!
198
+ langpair = [from == 'auto' ? '' : from, to].map { |e| e.tr('_', '-') }.join("|")
199
+ raw_json = Irc::Utils.bot.httputil.get_response(URI.escape(
200
+ "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=#{text}&langpair=#{langpair}")).body
201
+ response = JSON.parse(raw_json)
204
202
 
205
- @lang_list.value = "#{from}|#{to}".sub('_', '-')
206
- @form.fields.name('text').value = text
207
- @form.submit.parser.search('div#result_box').inner_html
203
+ if response["responseStatus"] != 200
204
+ raise Translator::NoTranslationError, response["responseDetails"]
205
+ else
206
+ translation = response["responseData"]["translatedText"]
207
+ return Utils.decode_html_entities(translation)
208
+ end
208
209
  end
209
210
  end
210
211
 
@@ -214,23 +215,31 @@ class BabelfishTranslator < Translator
214
215
 
215
216
  def initialize(cache)
216
217
  require 'mechanize'
217
-
218
- @form = WWW::Mechanize.new.get('http://babelfish.altavista.com/babelfish/').
219
- forms.name('frmTrText').first
220
- @lang_list = @form.fields.name('lp')
221
- language_pairs = @lang_list.options.map {|o| o.value.split('_')}.
222
- reject {|p| p.empty?}
218
+ (_, lang_list) = parse_page
219
+ language_pairs = lang_list.options.map {|o| o.value.split('_')}.
220
+ reject {|p| p.empty?}
223
221
  super(Translator::Direction.pairs(language_pairs), cache)
224
222
  end
225
223
 
224
+ def parse_page
225
+ form = WWW::Mechanize.new.get('http://babelfish.altavista.com/babelfish/').
226
+ forms_with(:name => 'frmTrText').first
227
+ lang_list = form.fields_with(:name => 'lp').first
228
+ [form, lang_list]
229
+ end
230
+
226
231
  def do_translate(text, from, to)
227
- if @form.fields.name('trtext').empty?
232
+ unless @form && @lang_list
233
+ @form, @lang_list = parse_page
234
+ end
235
+
236
+ if @form.fields_with(:name => 'trtext').empty?
228
237
  @form.add_field!('trtext', text)
229
238
  else
230
- @form.fields.name('trtext').value = text
239
+ @form.fields_with(:name => 'trtext').first.value = text
231
240
  end
232
241
  @lang_list.value = "#{from}_#{to}"
233
- @form.submit.parser.search("td.s/div[@style]").inner_html
242
+ @form.submit.parser.search("div[@id='result']/div[@style]").inner_html
234
243
  end
235
244
  end
236
245
 
@@ -260,6 +269,9 @@ class TranslatorPlugin < Plugin
260
269
  Config.register Config::IntegerValue.new('translator.timeout',
261
270
  :default => 30, :validate => Proc.new{|v| v > 0},
262
271
  :desc => _("Number of seconds to wait for the translation service before timeout"))
272
+ Config.register Config::StringValue.new('translator.destination',
273
+ :default => "en",
274
+ :desc => _("Default destination language to be used with translate command"))
263
275
 
264
276
  TRANSLATORS = {
265
277
  'nifty' => NiftyTranslator,
@@ -271,16 +283,13 @@ class TranslatorPlugin < Plugin
271
283
 
272
284
  def initialize
273
285
  super
274
-
286
+ @failed_translators = []
275
287
  @translators = {}
276
288
  TRANSLATORS.each_pair do |name, c|
277
- begin
289
+ watch_for_fail(name) do
278
290
  @translators[name] = c.new(@registry.sub_registry(name))
279
291
  map "#{name} :from :to *phrase",
280
292
  :action => :cmd_translate, :thread => true
281
- rescue Exception
282
- warning _("Translator %{name} cannot be used: %{reason}") %
283
- {:name => name, :reason => $!}
284
293
  end
285
294
  end
286
295
 
@@ -292,34 +301,95 @@ class TranslatorPlugin < Plugin
292
301
  update_default
293
302
  end
294
303
 
304
+ def watch_for_fail(name, &block)
305
+ begin
306
+ yield
307
+ rescue Exception
308
+ @failed_translators << { :name => name, :reason => $!.to_s }
309
+
310
+ warning _("Translator %{name} cannot be used: %{reason}") %
311
+ {:name => name, :reason => $!}
312
+ map "#{name} [*args]", :action => :failed_translator,
313
+ :defaults => {:name => name, :reason => $!}
314
+ end
315
+ end
316
+
317
+ def failed_translator(m, params)
318
+ m.reply _("Translator %{name} cannot be used: %{reason}") %
319
+ {:name => params[:name], :reason => params[:reason]}
320
+ end
321
+
295
322
  def help(plugin, topic=nil)
296
- if @translators.has_key?(plugin)
297
- translator = @translators[plugin]
298
- _('%{translator} <from> <to> <phrase> => Look up phrase using %{info}, supported from -> to languages: %{directions}') % {
299
- :translator => plugin,
300
- :info => translator.class::INFO,
301
- :directions => translator.directions.map do |source, targets|
302
- _('%{source} -> %{targets}') %
303
- {:source => source, :targets => targets.to_a.join(', ')}
304
- end.join(' | ')
305
- }
323
+ case (topic.intern rescue nil)
324
+ when :failed
325
+ unless @failed_translators.empty?
326
+ failed_list = @failed_translators.map { |t| _("%{bold}%{translator}%{bold}: %{reason}") % {
327
+ :translator => t[:name],
328
+ :reason => t[:reason],
329
+ :bold => Bold
330
+ }}
331
+
332
+ _("Failed translators: %{list}") % { :list => failed_list.join(", ") }
333
+ else
334
+ _("None of the translators failed")
335
+ end
306
336
  else
307
- _('Command: <translator> <from> <to> <phrase>, where <translator> is one of: %{translators}. If "translator" is used in place of the translator name, the first translator in translator.default_list which supports the specified direction will be picked automatically. Use "help <translator>" to look up supported from and to languages') %
308
- {:translators => @translators.keys.join(', ')}
337
+ if @translators.has_key?(plugin)
338
+ translator = @translators[plugin]
339
+ _('%{translator} <from> <to> <phrase> => Look up phrase using %{info}, supported from -> to languages: %{directions}') % {
340
+ :translator => plugin,
341
+ :info => translator.class::INFO,
342
+ :directions => translator.directions.map do |source, targets|
343
+ _('%{source} -> %{targets}') %
344
+ {:source => source, :targets => targets.to_a.join(', ')}
345
+ end.join(' | ')
346
+ }
347
+ else
348
+ help_str = _('Command: <translator> <from> <to> <phrase>, where <translator> is one of: %{translators}. If "translator" is used in place of the translator name, the first translator in translator.default_list which supports the specified direction will be picked automatically. Use "help <translator>" to look up supported from and to languages') %
349
+ {:translators => @translators.keys.join(', ')}
350
+
351
+ help_str << "\n" + _("%{bold}Note%{bold}: %{failed_amt} translators failed, see %{reverse}%{prefix}help translate failed%{reverse} for details") % {
352
+ :failed_amt => @failed_translators.size,
353
+ :bold => Bold,
354
+ :reverse => Reverse,
355
+ :prefix => @bot.config['core.address_prefix'].first
356
+ }
357
+
358
+ help_str
359
+ end
309
360
  end
310
361
  end
311
362
 
363
+ def languages
364
+ @languages ||= @translators.map { |t| t.last.directions.keys }.flatten.uniq
365
+ end
366
+
312
367
  def update_default
313
- @default_translators = bot.config['translator.default_list'] & @translators.keys
368
+ @default_translators = bot.config['translator.default_list'] & @translators.keys
314
369
  end
315
370
 
316
371
  def cmd_translator(m, params)
317
- from, to = params[:from], params[:to]
318
- translator = @default_translators.find {|t| @translators[t].support?(from, to)}
372
+ params[:to] = @bot.config['translator.destination'] if params[:to].nil?
373
+ params[:from] ||= 'auto'
374
+ translator = @default_translators.find {|t| @translators[t].support?(params[:from], params[:to])}
375
+
319
376
  if translator
320
377
  cmd_translate m, params.merge({:translator => translator, :show_provider => true})
321
378
  else
322
- m.reply _('None of the default translators (translator.default_list) supports translating from %{source} to %{target}') % {:source => from, :target => to}
379
+ # When translate command is used without source language, "auto" as source
380
+ # language is assumed. It means that google translator is used and we let google
381
+ # figure out what the source language is.
382
+ #
383
+ # Problem is that the google translator will fail if the system that the bot is
384
+ # running on does not have the json gem installed.
385
+ if params[:from] == 'auto'
386
+ m.reply _("Unable to auto-detect source language due to broken google translator, see %{reverse}%{prefix}help translate failed%{reverse} for details") % {
387
+ :reverse => Reverse,
388
+ :prefix => @bot.config['core.address_prefix'].first
389
+ }
390
+ else
391
+ m.reply _('None of the default translators (translator.default_list) supports translating from %{source} to %{target}') % {:source => params[:from], :target => params[:to]}
392
+ end
323
393
  end
324
394
  end
325
395
 
@@ -329,25 +399,27 @@ class TranslatorPlugin < Plugin
329
399
  translator = @translators[tname]
330
400
  from, to, phrase = params[:from], params[:to], params[:phrase].to_s
331
401
  if translator
332
- begin
333
- translation = Timeout.timeout(@bot.config['translator.timeout']) do
334
- translator.translate(phrase, from, to)
402
+ watch_for_fail(tname) do
403
+ begin
404
+ translation = Timeout.timeout(@bot.config['translator.timeout']) do
405
+ translator.translate(phrase, from, to)
406
+ end
407
+ m.reply(if params[:show_provider]
408
+ _('%{translation} (provided by %{translator})') %
409
+ {:translation => translation, :translator => tname.gsub("_", " ")}
410
+ else
411
+ translation
412
+ end)
413
+
414
+ rescue Translator::UnsupportedDirectionError
415
+ m.reply _("%{translator} doesn't support translating from %{source} to %{target}") %
416
+ {:translator => tname, :source => from, :target => to}
417
+ rescue Translator::NoTranslationError
418
+ m.reply _('%{translator} failed to provide a translation') %
419
+ {:translator => tname}
420
+ rescue Timeout::Error
421
+ m.reply _('The translator timed out')
335
422
  end
336
- m.reply(if params[:show_provider]
337
- _('%{translation} (provided by %{translator})') %
338
- {:translation => translation, :translator => tname}
339
- else
340
- translation
341
- end)
342
-
343
- rescue Translator::UnsupportedDirectionError
344
- m.reply _("%{translator} doesn't support translating from %{source} to %{target}") %
345
- {:translator => tname, :source => from, :target => to}
346
- rescue Translator::NoTranslationError
347
- m.reply _('%{translator} failed to provide a translation') %
348
- {:translator => tname}
349
- rescue Timeout::Error
350
- m.reply _('The translator timed out')
351
423
  end
352
424
  else
353
425
  m.reply _('No translator called %{name}') % {:name => tname}
@@ -356,5 +428,9 @@ class TranslatorPlugin < Plugin
356
428
  end
357
429
 
358
430
  plugin = TranslatorPlugin.new
359
- plugin.map 'translator :from :to *phrase',
360
- :action => :cmd_translator, :thread => true
431
+ req = Hash[*%w(from to).map { |e| [e.to_sym, /#{plugin.languages.join("|")}/] }.flatten]
432
+
433
+ plugin.map 'translate [:from] [:to] *phrase',
434
+ :action => :cmd_translator, :thread => true, :requirements => req
435
+ plugin.map 'translator [:from] [:to] *phrase',
436
+ :action => :cmd_translator, :thread => true, :requirements => req
@@ -5,7 +5,7 @@ class TubePlugin < Plugin
5
5
  def help(plugin, topic="")
6
6
  "tube [district|circle|metropolitan|central|jubilee|bakerloo|waterlooandcity|hammersmithandcity|victoria|eastlondon|northern|piccadilly] => display tube service status for the specified line(Docklands Light Railway is not currently supported)" # , tube stations => list tube stations (not lines) with problems"
7
7
  end
8
-
8
+
9
9
  def tube(m, params)
10
10
  line = params[:line]
11
11
  tube_page = @bot.httputil.get('http://www.tfl.gov.uk/tfl/livetravelnews/realtime/tube/default.html')
@@ -45,7 +45,7 @@ class TubePlugin < Plugin
45
45
  stations_array.push $1
46
46
  end
47
47
  }
48
- if stations_array.empty?
48
+ if stations_array.empty?
49
49
  m.reply "There are no station-specific announcements"
50
50
  return
51
51
  else
@@ -0,0 +1,143 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: tumblr interface
5
+ #
6
+ # Author:: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
7
+ # Copyright:: (C) 2009 Giuseppe Bilotta
8
+ # License:: GPLv2
9
+ #
10
+ # Submit URLs to channel-specific tumblr accounts
11
+ #
12
+ # TODO reblog tumblr URLs
13
+ # TODO support video better (e.g. Vimeo or anything else with embed)
14
+ # TODO support image better (e.g. pages with a single big image)
15
+ # TODO customize caption/description format
16
+
17
+ require 'rexml/document'
18
+ require 'cgi'
19
+
20
+ class TumblrPlugin < Plugin
21
+ RBOT = CGI.escape("rbot #{$version.split.first}")
22
+ WRITE_URL = "http://www.tumblr.com/api/write"
23
+ LOGIN = "email=%{email}&password=%{pwd}&group=%{group}&format=markdown&generator=" + RBOT
24
+ PHOTO = "&type=photo&source=%{src}&click-through-url=%{src}"
25
+ VIDEO = "&type=video&embed=%{src}"
26
+ CAPTION = "&caption=%{desc}"
27
+ LINK = "&type=link&url=%{src}"
28
+ DESC = "&name=%{desc}"
29
+
30
+ def help(plugin, topic="")
31
+ case topic
32
+ when "configure"
33
+ "tumblr configure [<channel>]: show credentials used for channel <channel> (default: current). tumblr configure [<channel>] <email> <password> [<group>] => post links from channel <channel> (default: current) to group <group> (default: name of channel) using the given tumblr credentials"
34
+ when "deconfigure"
35
+ "tumblr deconfigure [<channel>]: forget credentials for channel <channel> (default: current)."
36
+ else
37
+ "post links, photos and videos to a channel-specific tumblr. topics: configure, deconfigure"
38
+ end
39
+ end
40
+
41
+ def event_url_added(url, options={})
42
+ return unless options.key? :channel
43
+ chan = options[:channel]
44
+ return unless @registry.key? chan
45
+
46
+ account = @registry[chan]
47
+
48
+ line = options[:ircline]
49
+ line = nil if line and line.empty?
50
+ if line and nick = options[:nick]
51
+ line = "<#{nick}> #{line}"
52
+ end
53
+
54
+ req = LOGIN % account
55
+ type = options[:htmlinfo][:headers]['content-type'].first rescue nil
56
+ case type
57
+ when /^image\/.*/
58
+ data = PHOTO
59
+ data << CAPTION if line
60
+ else
61
+ if url.match(%r{^http://(\w+\.)?youtube\.com/watch.*})
62
+ data = VIDEO
63
+ data << CAPTION if line
64
+ else
65
+ data = LINK
66
+ data << DESC if line
67
+ end
68
+ end
69
+ req << (data % { :src => CGI.escape(url), :desc => CGI.escape(line) })
70
+ debug "posting #{req.inspect}"
71
+ resp = @bot.httputil.post(WRITE_URL, req)
72
+ debug "tumblr response: #{resp.inspect}"
73
+ end
74
+
75
+ def configuration(m, params={})
76
+ channel = params[:channel] || m.channel
77
+ if not channel
78
+ m.reply _("Please specify a channel")
79
+ return
80
+ end
81
+ if not @registry.key? channel
82
+ m.reply _("No tumblr credentials set for %{chan}" % { :chan => channel })
83
+ return false
84
+ end
85
+
86
+ account = @registry[channel]
87
+
88
+ account[:pwd] = _("<hidden>") if m.public?
89
+ account[:chan] = channel
90
+
91
+ m.reply _("Links on %{chan} will go to %{group} using account %{email} and password %{pwd}" % account)
92
+ end
93
+
94
+ def deconfigure(m, params={})
95
+ channel = params[:channel] || m.channel
96
+ if not channel
97
+ m.reply _("Please specify a channel")
98
+ return
99
+ end
100
+ if not @registry.key? channel
101
+ m.reply _("No tumblr credentials set for %{chan}" % { :chan => channel })
102
+ return false
103
+ end
104
+
105
+ @registry.delete channel
106
+
107
+ m.reply _("Links on %{chan} will not be posted to tumblr anymore" % {:chan => channel})
108
+ end
109
+
110
+ def configure(m, params={})
111
+ channel = params[:channel] || m.channel
112
+ if not channel
113
+ m.reply _("Please specify a channel")
114
+ return
115
+ end
116
+ if @registry.key? channel
117
+ m.reply _("%{chan} already has credentials configured" % { :chan => channel })
118
+ else
119
+ group = params[:group] || Channel.npname(channel)
120
+ group << ".tumblr.com" unless group.match(/\.tumblr\.com/)
121
+ @registry[channel] = {
122
+ :email => CGI.escape(params[:email]),
123
+ :pwd => CGI.escape(params[:pwd]),
124
+ :group => CGI.escape(group)
125
+ }
126
+
127
+ end
128
+
129
+ return configuration(m, params)
130
+ end
131
+
132
+ end
133
+
134
+ plugin = TumblrPlugin.new
135
+
136
+ plugin.default_auth('*', false)
137
+
138
+ plugin.map 'tumblr configure [:channel]', :action => :configuration
139
+ plugin.map 'tumblr deconfigure [:channel]', :action => :deconfigure
140
+ plugin.map 'tumblr configure [:channel] :email :pwd [:group]',
141
+ :action => :configure,
142
+ :requirements => {:channel => Regexp::Irc::GEN_CHAN, :email => /.+@.+/, :group => /[A-Za-z-]+/}
143
+