rbot 0.9.10 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (706) hide show
  1. data/AUTHORS +22 -4
  2. data/COPYING +5 -18
  3. data/ChangeLog +396 -0
  4. data/README +23 -25
  5. data/REQUIREMENTS +58 -12
  6. data/Rakefile +215 -0
  7. data/bin/rbot +47 -25
  8. data/bin/rbot-remote +76 -0
  9. data/data/rbot/languages/french.lang +5 -5
  10. data/data/rbot/languages/german.lang +4 -4
  11. data/data/rbot/languages/italian.lang +53 -0
  12. data/data/rbot/languages/japanese.lang +43 -0
  13. data/data/rbot/languages/russian.lang +60 -60
  14. data/data/rbot/languages/traditional_chinese.lang +75 -0
  15. data/data/rbot/plugins/alias.rb +192 -0
  16. data/data/rbot/plugins/autoop.rb +74 -37
  17. data/data/rbot/plugins/autorejoin.rb +15 -5
  18. data/data/rbot/plugins/bans.rb +454 -150
  19. data/data/rbot/plugins/bash.rb +173 -31
  20. data/data/rbot/plugins/botsnack.rb +37 -0
  21. data/data/rbot/plugins/cal.rb +11 -2
  22. data/data/rbot/plugins/chanserv.rb +132 -0
  23. data/data/rbot/plugins/chucknorris.rb +2 -2
  24. data/data/rbot/plugins/chucknorris.yml.gz +0 -0
  25. data/data/rbot/plugins/debugger.rb +136 -0
  26. data/data/rbot/plugins/deepthoughts.rb +89 -95
  27. data/data/rbot/plugins/delicious.rb +109 -0
  28. data/data/rbot/plugins/dice.rb +77 -25
  29. data/data/rbot/plugins/dict.rb +243 -0
  30. data/data/rbot/plugins/dictclient.rb +211 -0
  31. data/data/rbot/plugins/digg.rb +2 -2
  32. data/data/rbot/plugins/excuse.rb +4 -4
  33. data/data/rbot/plugins/factoids.rb +506 -0
  34. data/data/rbot/plugins/figlet.rb +127 -18
  35. data/data/rbot/plugins/fish.rb +93 -33
  36. data/data/rbot/plugins/forecast.rb +40 -27
  37. data/data/rbot/plugins/fortune.rb +86 -11
  38. data/data/rbot/plugins/freshmeat.rb +46 -12
  39. data/data/rbot/plugins/games/azgame.rb +565 -0
  40. data/data/rbot/plugins/games/quiz.rb +961 -0
  41. data/data/rbot/plugins/games/roshambo.rb +62 -0
  42. data/data/rbot/plugins/{roulette.rb → games/roulette.rb} +58 -8
  43. data/data/rbot/plugins/games/shiritori.rb +485 -0
  44. data/data/rbot/plugins/games/uno.rb +1205 -0
  45. data/data/rbot/plugins/games/wheelfortune.rb +615 -0
  46. data/data/rbot/plugins/grouphug.rb +76 -22
  47. data/data/rbot/plugins/hl2.rb +97 -0
  48. data/data/rbot/plugins/host.rb +10 -1
  49. data/data/rbot/plugins/httpd.rb.disabled +1 -0
  50. data/data/rbot/plugins/imdb.rb +511 -47
  51. data/data/rbot/plugins/insult.rb +32 -37
  52. data/data/rbot/plugins/iplookup.rb +224 -224
  53. data/data/rbot/plugins/karma.rb +45 -21
  54. data/data/rbot/plugins/keywords.rb +258 -160
  55. data/data/rbot/plugins/lart.rb +126 -95
  56. data/data/rbot/plugins/lastfm.rb +421 -12
  57. data/data/rbot/plugins/linkbot.rb +75 -0
  58. data/data/rbot/plugins/markov.rb +103 -46
  59. data/data/rbot/plugins/math.rb +6 -5
  60. data/data/rbot/plugins/modes.rb +114 -0
  61. data/data/rbot/plugins/nickrecover.rb +97 -0
  62. data/data/rbot/plugins/nickserv.rb +111 -48
  63. data/data/rbot/plugins/nslookup.rb +15 -19
  64. data/data/rbot/plugins/plugin.header +11 -0
  65. data/data/rbot/plugins/quakeauth.rb +74 -11
  66. data/data/rbot/plugins/quotes.rb +189 -233
  67. data/data/rbot/plugins/reaction.rb +415 -0
  68. data/data/rbot/plugins/remind.rb +11 -15
  69. data/data/rbot/plugins/remotectl.rb +29 -0
  70. data/data/rbot/plugins/ri.rb +74 -0
  71. data/data/rbot/plugins/rot13.rb +16 -1
  72. data/data/rbot/plugins/rss.rb +1219 -585
  73. data/data/rbot/plugins/salut.rb +226 -0
  74. data/data/rbot/plugins/script.rb +184 -0
  75. data/data/rbot/plugins/search.rb +233 -0
  76. data/data/rbot/plugins/seen.rb +44 -40
  77. data/data/rbot/plugins/shortenurls.rb +93 -0
  78. data/data/rbot/plugins/slashdot.rb +49 -11
  79. data/data/rbot/plugins/spell.rb +37 -27
  80. data/data/rbot/plugins/theyfightcrime.rb +36 -39
  81. data/data/rbot/plugins/threat.rb +25 -22
  82. data/data/rbot/plugins/time.rb +135 -0
  83. data/data/rbot/plugins/topic.rb +107 -50
  84. data/data/rbot/plugins/translator.rb +360 -0
  85. data/data/rbot/plugins/tube.rb +28 -40
  86. data/data/rbot/plugins/twitter.rb +187 -0
  87. data/data/rbot/plugins/urban.rb +62 -53
  88. data/data/rbot/plugins/url.rb +193 -409
  89. data/data/rbot/plugins/usermodes.rb +26 -0
  90. data/data/rbot/plugins/wall.rb +60 -0
  91. data/data/rbot/plugins/weather.rb +254 -598
  92. data/data/rbot/plugins/wow.rb +41 -37
  93. data/data/rbot/plugins/wserver.rb +17 -25
  94. data/data/rbot/plugins/youtube.rb +237 -0
  95. data/data/rbot/templates/lart/{larts → larts-english} +0 -0
  96. data/data/rbot/templates/lart/larts-italian +25 -0
  97. data/data/rbot/templates/lart/larts-japanese +10 -0
  98. data/data/rbot/templates/lart/{praises → praises-english} +0 -0
  99. data/data/rbot/templates/lart/praises-italian +4 -0
  100. data/data/rbot/templates/lart/praises-japanese +3 -0
  101. data/data/rbot/templates/quiz/win_messages +11 -0
  102. data/data/rbot/templates/salut/salut-english +40 -0
  103. data/data/rbot/templates/salut/salut-french +29 -0
  104. data/data/rbot/templates/salut/salut-italian +51 -0
  105. data/data/rbot/templates/salut/salut-japanese +31 -0
  106. data/lib/rbot/botuser.rb +940 -0
  107. data/lib/rbot/config-compat.rb +23 -0
  108. data/lib/rbot/config.rb +211 -276
  109. data/lib/rbot/core/auth.rb +1063 -0
  110. data/lib/rbot/core/basics.rb +214 -0
  111. data/lib/rbot/core/config.rb +330 -0
  112. data/lib/rbot/core/filters_ui.rb +64 -0
  113. data/lib/rbot/core/irclog.rb +292 -0
  114. data/lib/rbot/core/remote.rb +401 -0
  115. data/lib/rbot/core/unicode.rb +91 -0
  116. data/lib/rbot/core/userdata.rb +200 -0
  117. data/lib/rbot/core/utils/extends.rb +444 -0
  118. data/lib/rbot/core/utils/filters.rb +154 -0
  119. data/lib/rbot/core/utils/httputil.rb +689 -0
  120. data/lib/rbot/core/utils/utils.rb +717 -0
  121. data/lib/rbot/dbhash.rb +30 -9
  122. data/lib/rbot/irc.rb +1966 -0
  123. data/lib/rbot/ircbot.rb +816 -675
  124. data/lib/rbot/ircsocket.rb +246 -232
  125. data/lib/rbot/language.rb +86 -8
  126. data/lib/rbot/load-gettext.rb +154 -0
  127. data/lib/rbot/maskdb.rb +162 -0
  128. data/lib/rbot/message.rb +392 -65
  129. data/lib/rbot/messagemapper.rb +438 -94
  130. data/lib/rbot/pkgconfig.rb +6 -0
  131. data/lib/rbot/plugins.rb +713 -172
  132. data/lib/rbot/post-config.rb +1 -0
  133. data/lib/rbot/rbotconfig.rb +31 -12
  134. data/lib/rbot/registry.rb +79 -48
  135. data/lib/rbot/rfc2812.rb +927 -519
  136. data/lib/rbot/timer.rb +232 -167
  137. data/po/en_US/rbot-alias.po +82 -0
  138. data/po/en_US/rbot-autoop.po +0 -0
  139. data/po/en_US/rbot-autorejoin.po +0 -0
  140. data/po/en_US/rbot-azgame.po +200 -0
  141. data/po/en_US/rbot-bans.po +0 -0
  142. data/po/en_US/rbot-bash.po +0 -0
  143. data/po/en_US/rbot-botsnack.po +0 -0
  144. data/po/en_US/rbot-cal.po +3 -0
  145. data/po/en_US/rbot-chanserv.po +0 -0
  146. data/po/en_US/rbot-chucknorris.po +0 -0
  147. data/po/en_US/rbot-debugger.po +0 -0
  148. data/po/en_US/rbot-deepthoughts.po +0 -0
  149. data/po/en_US/rbot-delicious.po +0 -0
  150. data/po/en_US/rbot-dice.po +0 -0
  151. data/po/en_US/rbot-dict.po +0 -0
  152. data/po/en_US/rbot-dictclient.po +124 -0
  153. data/po/en_US/rbot-digg.po +0 -0
  154. data/po/en_US/rbot-eightball.po +0 -0
  155. data/po/en_US/rbot-excuse.po +0 -0
  156. data/po/en_US/rbot-factoids.po +107 -0
  157. data/po/en_US/rbot-figlet.po +36 -0
  158. data/po/en_US/rbot-fish.po +0 -0
  159. data/po/en_US/rbot-forecast.po +0 -0
  160. data/po/en_US/rbot-fortune.po +0 -0
  161. data/po/en_US/rbot-freshmeat.po +0 -0
  162. data/po/en_US/rbot-grouphug.po +18 -0
  163. data/po/en_US/rbot-hl2.po +0 -0
  164. data/po/en_US/rbot-host.po +3 -0
  165. data/po/en_US/rbot-imdb.po +0 -0
  166. data/po/en_US/rbot-insult.po +0 -0
  167. data/po/en_US/rbot-iplookup.po +0 -0
  168. data/po/en_US/rbot-karma.po +0 -0
  169. data/po/en_US/rbot-keywords.po +24 -0
  170. data/po/en_US/rbot-lart.po +0 -0
  171. data/po/en_US/rbot-lastfm.po +172 -0
  172. data/po/en_US/rbot-linkbot.po +0 -0
  173. data/po/en_US/rbot-markov.po +20 -0
  174. data/po/en_US/rbot-math.po +0 -0
  175. data/po/en_US/rbot-modes.po +0 -0
  176. data/po/en_US/rbot-nickrecover.po +40 -0
  177. data/po/en_US/rbot-nickserv.po +104 -0
  178. data/po/en_US/rbot-nslookup.po +0 -0
  179. data/po/en_US/rbot-quakeauth.po +0 -0
  180. data/po/en_US/rbot-quiz.po +0 -0
  181. data/po/en_US/rbot-quotes.po +108 -0
  182. data/po/en_US/rbot-reaction.po +0 -0
  183. data/po/en_US/rbot-remind.po +0 -0
  184. data/po/en_US/rbot-remotectl.po +0 -0
  185. data/po/en_US/rbot-ri.po +0 -0
  186. data/po/en_US/rbot-roshambo.po +0 -0
  187. data/po/en_US/rbot-rot13.po +0 -0
  188. data/po/en_US/rbot-roulette.po +0 -0
  189. data/po/en_US/rbot-rss.po +20 -0
  190. data/po/en_US/rbot-salut.po +0 -0
  191. data/po/en_US/rbot-script.po +0 -0
  192. data/po/en_US/rbot-search.po +0 -0
  193. data/po/en_US/rbot-seen.po +0 -0
  194. data/po/en_US/rbot-shiritori.po +113 -0
  195. data/po/en_US/rbot-shortenurls.po +0 -0
  196. data/po/en_US/rbot-slashdot.po +0 -0
  197. data/po/en_US/rbot-spell.po +54 -0
  198. data/po/en_US/rbot-theyfightcrime.po +0 -0
  199. data/po/en_US/rbot-threat.po +0 -0
  200. data/po/en_US/rbot-time.po +0 -0
  201. data/po/en_US/rbot-topic.po +0 -0
  202. data/po/en_US/rbot-translator.po +77 -0
  203. data/po/en_US/rbot-tube.po +0 -0
  204. data/po/en_US/rbot-twitter.po +24 -0
  205. data/po/en_US/rbot-uno.po +512 -0
  206. data/po/en_US/rbot-urban.po +0 -0
  207. data/po/en_US/rbot-url.po +0 -0
  208. data/po/en_US/rbot-usermodes.po +0 -0
  209. data/po/en_US/rbot-wall.po +33 -0
  210. data/po/en_US/rbot-weather.po +0 -0
  211. data/po/en_US/rbot-wheelfortune.po +205 -0
  212. data/po/en_US/rbot-wow.po +0 -0
  213. data/po/en_US/rbot-wserver.po +0 -0
  214. data/po/en_US/rbot-youtube.po +58 -0
  215. data/po/en_US/rbot.po +1097 -0
  216. data/po/fr/rbot-alias.po +97 -0
  217. data/po/fr/rbot-autoop.po +0 -0
  218. data/po/fr/rbot-autorejoin.po +0 -0
  219. data/po/fr/rbot-azgame.po +204 -0
  220. data/po/fr/rbot-bans.po +0 -0
  221. data/po/fr/rbot-bash.po +0 -0
  222. data/po/fr/rbot-botsnack.po +0 -0
  223. data/po/fr/rbot-cal.po +3 -0
  224. data/po/fr/rbot-chanserv.po +0 -0
  225. data/po/fr/rbot-chucknorris.po +0 -0
  226. data/po/fr/rbot-debugger.po +0 -0
  227. data/po/fr/rbot-deepthoughts.po +0 -0
  228. data/po/fr/rbot-delicious.po +0 -0
  229. data/po/fr/rbot-dice.po +0 -0
  230. data/po/fr/rbot-dict.po +0 -0
  231. data/po/fr/rbot-dictclient.po +134 -0
  232. data/po/fr/rbot-digg.po +0 -0
  233. data/po/fr/rbot-eightball.po +0 -0
  234. data/po/fr/rbot-excuse.po +0 -0
  235. data/po/fr/rbot-factoids.po +111 -0
  236. data/po/fr/rbot-figlet.po +37 -0
  237. data/po/fr/rbot-fish.po +0 -0
  238. data/po/fr/rbot-forecast.po +0 -0
  239. data/po/fr/rbot-fortune.po +0 -0
  240. data/po/fr/rbot-freshmeat.po +0 -0
  241. data/po/fr/rbot-grouphug.po +18 -0
  242. data/po/fr/rbot-hl2.po +0 -0
  243. data/po/fr/rbot-host.po +3 -0
  244. data/po/fr/rbot-imdb.po +0 -0
  245. data/po/fr/rbot-insult.po +0 -0
  246. data/po/fr/rbot-iplookup.po +0 -0
  247. data/po/fr/rbot-karma.po +0 -0
  248. data/po/fr/rbot-keywords.po +24 -0
  249. data/po/fr/rbot-lart.po +0 -0
  250. data/po/fr/rbot-lastfm.po +172 -0
  251. data/po/fr/rbot-linkbot.po +0 -0
  252. data/po/fr/rbot-markov.po +20 -0
  253. data/po/fr/rbot-math.po +0 -0
  254. data/po/fr/rbot-modes.po +0 -0
  255. data/po/fr/rbot-nickrecover.po +36 -0
  256. data/po/fr/rbot-nickserv.po +116 -0
  257. data/po/fr/rbot-nslookup.po +0 -0
  258. data/po/fr/rbot-quakeauth.po +0 -0
  259. data/po/fr/rbot-quiz.po +0 -0
  260. data/po/fr/rbot-quotes.po +138 -0
  261. data/po/fr/rbot-reaction.po +0 -0
  262. data/po/fr/rbot-remind.po +0 -0
  263. data/po/fr/rbot-remotectl.po +0 -0
  264. data/po/fr/rbot-ri.po +0 -0
  265. data/po/fr/rbot-roshambo.po +0 -0
  266. data/po/fr/rbot-rot13.po +0 -0
  267. data/po/fr/rbot-roulette.po +0 -0
  268. data/po/fr/rbot-rss.po +20 -0
  269. data/po/fr/rbot-salut.po +0 -0
  270. data/po/fr/rbot-script.po +0 -0
  271. data/po/fr/rbot-search.po +0 -0
  272. data/po/fr/rbot-seen.po +0 -0
  273. data/po/fr/rbot-shiritori.po +119 -0
  274. data/po/fr/rbot-shortenurls.po +0 -0
  275. data/po/fr/rbot-slashdot.po +0 -0
  276. data/po/fr/rbot-spell.po +56 -0
  277. data/po/fr/rbot-theyfightcrime.po +0 -0
  278. data/po/fr/rbot-threat.po +0 -0
  279. data/po/fr/rbot-time.po +0 -0
  280. data/po/fr/rbot-topic.po +0 -0
  281. data/po/fr/rbot-translator.po +90 -0
  282. data/po/fr/rbot-tube.po +0 -0
  283. data/po/fr/rbot-twitter.po +24 -0
  284. data/po/fr/rbot-uno.po +521 -0
  285. data/po/fr/rbot-urban.po +0 -0
  286. data/po/fr/rbot-url.po +0 -0
  287. data/po/fr/rbot-usermodes.po +0 -0
  288. data/po/fr/rbot-wall.po +33 -0
  289. data/po/fr/rbot-weather.po +0 -0
  290. data/po/fr/rbot-wheelfortune.po +255 -0
  291. data/po/fr/rbot-wow.po +0 -0
  292. data/po/fr/rbot-wserver.po +0 -0
  293. data/po/fr/rbot-youtube.po +64 -0
  294. data/po/fr/rbot.po +1133 -0
  295. data/po/it/rbot-alias.po +82 -0
  296. data/po/it/rbot-autoop.po +0 -0
  297. data/po/it/rbot-autorejoin.po +0 -0
  298. data/po/it/rbot-azgame.po +186 -0
  299. data/po/it/rbot-bans.po +0 -0
  300. data/po/it/rbot-bash.po +0 -0
  301. data/po/it/rbot-botsnack.po +0 -0
  302. data/po/it/rbot-cal.po +3 -0
  303. data/po/it/rbot-chanserv.po +0 -0
  304. data/po/it/rbot-chucknorris.po +0 -0
  305. data/po/it/rbot-debugger.po +0 -0
  306. data/po/it/rbot-deepthoughts.po +0 -0
  307. data/po/it/rbot-delicious.po +0 -0
  308. data/po/it/rbot-dice.po +0 -0
  309. data/po/it/rbot-dict.po +0 -0
  310. data/po/it/rbot-dictclient.po +111 -0
  311. data/po/it/rbot-digg.po +0 -0
  312. data/po/it/rbot-eightball.po +0 -0
  313. data/po/it/rbot-excuse.po +0 -0
  314. data/po/it/rbot-factoids.po +110 -0
  315. data/po/it/rbot-figlet.po +36 -0
  316. data/po/it/rbot-fish.po +0 -0
  317. data/po/it/rbot-forecast.po +0 -0
  318. data/po/it/rbot-fortune.po +0 -0
  319. data/po/it/rbot-freshmeat.po +0 -0
  320. data/po/it/rbot-grouphug.po +18 -0
  321. data/po/it/rbot-hl2.po +0 -0
  322. data/po/it/rbot-host.po +3 -0
  323. data/po/it/rbot-imdb.po +0 -0
  324. data/po/it/rbot-insult.po +0 -0
  325. data/po/it/rbot-iplookup.po +0 -0
  326. data/po/it/rbot-karma.po +0 -0
  327. data/po/it/rbot-keywords.po +24 -0
  328. data/po/it/rbot-lart.po +0 -0
  329. data/po/it/rbot-lastfm.po +172 -0
  330. data/po/it/rbot-linkbot.po +0 -0
  331. data/po/it/rbot-markov.po +20 -0
  332. data/po/it/rbot-math.po +0 -0
  333. data/po/it/rbot-modes.po +0 -0
  334. data/po/it/rbot-nickrecover.po +36 -0
  335. data/po/it/rbot-nickserv.po +104 -0
  336. data/po/it/rbot-nslookup.po +0 -0
  337. data/po/it/rbot-quakeauth.po +0 -0
  338. data/po/it/rbot-quiz.po +0 -0
  339. data/po/it/rbot-quotes.po +108 -0
  340. data/po/it/rbot-reaction.po +0 -0
  341. data/po/it/rbot-remind.po +0 -0
  342. data/po/it/rbot-remotectl.po +0 -0
  343. data/po/it/rbot-ri.po +0 -0
  344. data/po/it/rbot-roshambo.po +0 -0
  345. data/po/it/rbot-rot13.po +0 -0
  346. data/po/it/rbot-roulette.po +0 -0
  347. data/po/it/rbot-rss.po +20 -0
  348. data/po/it/rbot-salut.po +0 -0
  349. data/po/it/rbot-script.po +0 -0
  350. data/po/it/rbot-search.po +0 -0
  351. data/po/it/rbot-seen.po +0 -0
  352. data/po/it/rbot-shiritori.po +102 -0
  353. data/po/it/rbot-shortenurls.po +0 -0
  354. data/po/it/rbot-slashdot.po +0 -0
  355. data/po/it/rbot-spell.po +54 -0
  356. data/po/it/rbot-theyfightcrime.po +0 -0
  357. data/po/it/rbot-threat.po +0 -0
  358. data/po/it/rbot-time.po +0 -0
  359. data/po/it/rbot-topic.po +0 -0
  360. data/po/it/rbot-translator.po +77 -0
  361. data/po/it/rbot-tube.po +0 -0
  362. data/po/it/rbot-twitter.po +24 -0
  363. data/po/it/rbot-uno.po +564 -0
  364. data/po/it/rbot-urban.po +0 -0
  365. data/po/it/rbot-url.po +0 -0
  366. data/po/it/rbot-usermodes.po +0 -0
  367. data/po/it/rbot-wall.po +33 -0
  368. data/po/it/rbot-weather.po +0 -0
  369. data/po/it/rbot-wheelfortune.po +251 -0
  370. data/po/it/rbot-wow.po +0 -0
  371. data/po/it/rbot-wserver.po +0 -0
  372. data/po/it/rbot-youtube.po +64 -0
  373. data/po/it/rbot.po +1127 -0
  374. data/po/ja/rbot-alias.po +82 -0
  375. data/po/ja/rbot-autoop.po +0 -0
  376. data/po/ja/rbot-autorejoin.po +0 -0
  377. data/po/ja/rbot-azgame.po +194 -0
  378. data/po/ja/rbot-bans.po +0 -0
  379. data/po/ja/rbot-bash.po +0 -0
  380. data/po/ja/rbot-botsnack.po +0 -0
  381. data/po/ja/rbot-cal.po +3 -0
  382. data/po/ja/rbot-chanserv.po +0 -0
  383. data/po/ja/rbot-chucknorris.po +0 -0
  384. data/po/ja/rbot-debugger.po +0 -0
  385. data/po/ja/rbot-deepthoughts.po +0 -0
  386. data/po/ja/rbot-delicious.po +0 -0
  387. data/po/ja/rbot-dice.po +0 -0
  388. data/po/ja/rbot-dict.po +0 -0
  389. data/po/ja/rbot-dictclient.po +111 -0
  390. data/po/ja/rbot-digg.po +0 -0
  391. data/po/ja/rbot-eightball.po +0 -0
  392. data/po/ja/rbot-excuse.po +0 -0
  393. data/po/ja/rbot-factoids.po +107 -0
  394. data/po/ja/rbot-figlet.po +36 -0
  395. data/po/ja/rbot-fish.po +0 -0
  396. data/po/ja/rbot-forecast.po +0 -0
  397. data/po/ja/rbot-fortune.po +0 -0
  398. data/po/ja/rbot-freshmeat.po +0 -0
  399. data/po/ja/rbot-grouphug.po +18 -0
  400. data/po/ja/rbot-hl2.po +0 -0
  401. data/po/ja/rbot-host.po +3 -0
  402. data/po/ja/rbot-imdb.po +0 -0
  403. data/po/ja/rbot-insult.po +0 -0
  404. data/po/ja/rbot-iplookup.po +0 -0
  405. data/po/ja/rbot-karma.po +0 -0
  406. data/po/ja/rbot-keywords.po +24 -0
  407. data/po/ja/rbot-lart.po +0 -0
  408. data/po/ja/rbot-lastfm.po +172 -0
  409. data/po/ja/rbot-linkbot.po +0 -0
  410. data/po/ja/rbot-markov.po +20 -0
  411. data/po/ja/rbot-math.po +0 -0
  412. data/po/ja/rbot-modes.po +0 -0
  413. data/po/ja/rbot-nickrecover.po +36 -0
  414. data/po/ja/rbot-nickserv.po +104 -0
  415. data/po/ja/rbot-nslookup.po +0 -0
  416. data/po/ja/rbot-quakeauth.po +0 -0
  417. data/po/ja/rbot-quiz.po +0 -0
  418. data/po/ja/rbot-quotes.po +108 -0
  419. data/po/ja/rbot-reaction.po +0 -0
  420. data/po/ja/rbot-remind.po +0 -0
  421. data/po/ja/rbot-remotectl.po +0 -0
  422. data/po/ja/rbot-ri.po +0 -0
  423. data/po/ja/rbot-roshambo.po +0 -0
  424. data/po/ja/rbot-rot13.po +0 -0
  425. data/po/ja/rbot-roulette.po +0 -0
  426. data/po/ja/rbot-rss.po +20 -0
  427. data/po/ja/rbot-salut.po +0 -0
  428. data/po/ja/rbot-script.po +0 -0
  429. data/po/ja/rbot-search.po +0 -0
  430. data/po/ja/rbot-seen.po +0 -0
  431. data/po/ja/rbot-shiritori.po +115 -0
  432. data/po/ja/rbot-shortenurls.po +0 -0
  433. data/po/ja/rbot-slashdot.po +0 -0
  434. data/po/ja/rbot-spell.po +54 -0
  435. data/po/ja/rbot-theyfightcrime.po +0 -0
  436. data/po/ja/rbot-threat.po +0 -0
  437. data/po/ja/rbot-time.po +0 -0
  438. data/po/ja/rbot-topic.po +0 -0
  439. data/po/ja/rbot-translator.po +77 -0
  440. data/po/ja/rbot-tube.po +0 -0
  441. data/po/ja/rbot-twitter.po +24 -0
  442. data/po/ja/rbot-uno.po +512 -0
  443. data/po/ja/rbot-urban.po +0 -0
  444. data/po/ja/rbot-url.po +0 -0
  445. data/po/ja/rbot-usermodes.po +0 -0
  446. data/po/ja/rbot-wall.po +33 -0
  447. data/po/ja/rbot-weather.po +0 -0
  448. data/po/ja/rbot-wheelfortune.po +205 -0
  449. data/po/ja/rbot-wow.po +0 -0
  450. data/po/ja/rbot-wserver.po +0 -0
  451. data/po/ja/rbot-youtube.po +58 -0
  452. data/po/ja/rbot.po +999 -0
  453. data/po/rbot-alias.pot +83 -0
  454. data/po/rbot-autoop.pot +0 -0
  455. data/po/rbot-autorejoin.pot +0 -0
  456. data/po/rbot-azgame.pot +187 -0
  457. data/po/rbot-bans.pot +0 -0
  458. data/po/rbot-bash.pot +0 -0
  459. data/po/rbot-botsnack.pot +0 -0
  460. data/po/rbot-cal.pot +21 -0
  461. data/po/rbot-chanserv.pot +0 -0
  462. data/po/rbot-chucknorris.pot +0 -0
  463. data/po/rbot-debugger.pot +0 -0
  464. data/po/rbot-deepthoughts.pot +0 -0
  465. data/po/rbot-delicious.pot +0 -0
  466. data/po/rbot-dice.pot +0 -0
  467. data/po/rbot-dict.pot +0 -0
  468. data/po/rbot-dictclient.pot +112 -0
  469. data/po/rbot-digg.pot +0 -0
  470. data/po/rbot-eightball.pot +0 -0
  471. data/po/rbot-excuse.pot +0 -0
  472. data/po/rbot-factoids.pot +108 -0
  473. data/po/rbot-figlet.pot +37 -0
  474. data/po/rbot-fish.pot +0 -0
  475. data/po/rbot-forecast.pot +0 -0
  476. data/po/rbot-fortune.pot +0 -0
  477. data/po/rbot-freshmeat.pot +0 -0
  478. data/po/rbot-grouphug.pot +36 -0
  479. data/po/rbot-hl2.pot +0 -0
  480. data/po/rbot-host.pot +21 -0
  481. data/po/rbot-imdb.pot +0 -0
  482. data/po/rbot-insult.pot +0 -0
  483. data/po/rbot-iplookup.pot +0 -0
  484. data/po/rbot-karma.pot +0 -0
  485. data/po/rbot-keywords.pot +25 -0
  486. data/po/rbot-lart.pot +0 -0
  487. data/po/rbot-lastfm.pot +190 -0
  488. data/po/rbot-linkbot.pot +0 -0
  489. data/po/rbot-markov.pot +21 -0
  490. data/po/rbot-math.pot +0 -0
  491. data/po/rbot-modes.pot +0 -0
  492. data/po/rbot-nickrecover.pot +37 -0
  493. data/po/rbot-nickserv.pot +105 -0
  494. data/po/rbot-nslookup.pot +0 -0
  495. data/po/rbot-quakeauth.pot +0 -0
  496. data/po/rbot-quiz.pot +0 -0
  497. data/po/rbot-quotes.pot +109 -0
  498. data/po/rbot-reaction.pot +0 -0
  499. data/po/rbot-remind.pot +0 -0
  500. data/po/rbot-remotectl.pot +0 -0
  501. data/po/rbot-ri.pot +0 -0
  502. data/po/rbot-roshambo.pot +0 -0
  503. data/po/rbot-rot13.pot +0 -0
  504. data/po/rbot-roulette.pot +0 -0
  505. data/po/rbot-rss.pot +21 -0
  506. data/po/rbot-salut.pot +0 -0
  507. data/po/rbot-script.pot +0 -0
  508. data/po/rbot-search.pot +0 -0
  509. data/po/rbot-seen.pot +0 -0
  510. data/po/rbot-shiritori.pot +103 -0
  511. data/po/rbot-shortenurls.pot +0 -0
  512. data/po/rbot-slashdot.pot +0 -0
  513. data/po/rbot-spell.pot +55 -0
  514. data/po/rbot-theyfightcrime.pot +0 -0
  515. data/po/rbot-threat.pot +0 -0
  516. data/po/rbot-time.pot +0 -0
  517. data/po/rbot-topic.pot +0 -0
  518. data/po/rbot-translator.pot +78 -0
  519. data/po/rbot-tube.pot +0 -0
  520. data/po/rbot-twitter.pot +25 -0
  521. data/po/rbot-uno.pot +513 -0
  522. data/po/rbot-urban.pot +0 -0
  523. data/po/rbot-url.pot +0 -0
  524. data/po/rbot-usermodes.pot +0 -0
  525. data/po/rbot-wall.pot +33 -0
  526. data/po/rbot-weather.pot +0 -0
  527. data/po/rbot-wheelfortune.pot +206 -0
  528. data/po/rbot-wow.pot +0 -0
  529. data/po/rbot-wserver.pot +0 -0
  530. data/po/rbot-youtube.pot +59 -0
  531. data/po/rbot.pot +1004 -0
  532. data/po/zh_CN/rbot-alias.po +82 -0
  533. data/po/zh_CN/rbot-autoop.po +0 -0
  534. data/po/zh_CN/rbot-autorejoin.po +0 -0
  535. data/po/zh_CN/rbot-azgame.po +186 -0
  536. data/po/zh_CN/rbot-bans.po +0 -0
  537. data/po/zh_CN/rbot-bash.po +0 -0
  538. data/po/zh_CN/rbot-botsnack.po +0 -0
  539. data/po/zh_CN/rbot-cal.po +3 -0
  540. data/po/zh_CN/rbot-chanserv.po +0 -0
  541. data/po/zh_CN/rbot-chucknorris.po +0 -0
  542. data/po/zh_CN/rbot-debugger.po +0 -0
  543. data/po/zh_CN/rbot-deepthoughts.po +0 -0
  544. data/po/zh_CN/rbot-delicious.po +0 -0
  545. data/po/zh_CN/rbot-dice.po +0 -0
  546. data/po/zh_CN/rbot-dict.po +0 -0
  547. data/po/zh_CN/rbot-dictclient.po +111 -0
  548. data/po/zh_CN/rbot-digg.po +0 -0
  549. data/po/zh_CN/rbot-eightball.po +0 -0
  550. data/po/zh_CN/rbot-excuse.po +0 -0
  551. data/po/zh_CN/rbot-factoids.po +107 -0
  552. data/po/zh_CN/rbot-figlet.po +36 -0
  553. data/po/zh_CN/rbot-fish.po +0 -0
  554. data/po/zh_CN/rbot-forecast.po +0 -0
  555. data/po/zh_CN/rbot-fortune.po +0 -0
  556. data/po/zh_CN/rbot-freshmeat.po +0 -0
  557. data/po/zh_CN/rbot-grouphug.po +18 -0
  558. data/po/zh_CN/rbot-hl2.po +0 -0
  559. data/po/zh_CN/rbot-host.po +3 -0
  560. data/po/zh_CN/rbot-imdb.po +0 -0
  561. data/po/zh_CN/rbot-insult.po +0 -0
  562. data/po/zh_CN/rbot-iplookup.po +0 -0
  563. data/po/zh_CN/rbot-karma.po +0 -0
  564. data/po/zh_CN/rbot-keywords.po +24 -0
  565. data/po/zh_CN/rbot-lart.po +0 -0
  566. data/po/zh_CN/rbot-lastfm.po +172 -0
  567. data/po/zh_CN/rbot-linkbot.po +0 -0
  568. data/po/zh_CN/rbot-markov.po +20 -0
  569. data/po/zh_CN/rbot-math.po +0 -0
  570. data/po/zh_CN/rbot-modes.po +0 -0
  571. data/po/zh_CN/rbot-nickrecover.po +36 -0
  572. data/po/zh_CN/rbot-nickserv.po +104 -0
  573. data/po/zh_CN/rbot-nslookup.po +0 -0
  574. data/po/zh_CN/rbot-quakeauth.po +0 -0
  575. data/po/zh_CN/rbot-quiz.po +0 -0
  576. data/po/zh_CN/rbot-quotes.po +108 -0
  577. data/po/zh_CN/rbot-reaction.po +0 -0
  578. data/po/zh_CN/rbot-remind.po +0 -0
  579. data/po/zh_CN/rbot-remotectl.po +0 -0
  580. data/po/zh_CN/rbot-ri.po +0 -0
  581. data/po/zh_CN/rbot-roshambo.po +0 -0
  582. data/po/zh_CN/rbot-rot13.po +0 -0
  583. data/po/zh_CN/rbot-roulette.po +0 -0
  584. data/po/zh_CN/rbot-rss.po +20 -0
  585. data/po/zh_CN/rbot-salut.po +0 -0
  586. data/po/zh_CN/rbot-script.po +0 -0
  587. data/po/zh_CN/rbot-search.po +0 -0
  588. data/po/zh_CN/rbot-seen.po +0 -0
  589. data/po/zh_CN/rbot-shiritori.po +102 -0
  590. data/po/zh_CN/rbot-shortenurls.po +0 -0
  591. data/po/zh_CN/rbot-slashdot.po +0 -0
  592. data/po/zh_CN/rbot-spell.po +54 -0
  593. data/po/zh_CN/rbot-theyfightcrime.po +0 -0
  594. data/po/zh_CN/rbot-threat.po +0 -0
  595. data/po/zh_CN/rbot-time.po +0 -0
  596. data/po/zh_CN/rbot-topic.po +0 -0
  597. data/po/zh_CN/rbot-translator.po +77 -0
  598. data/po/zh_CN/rbot-tube.po +0 -0
  599. data/po/zh_CN/rbot-twitter.po +24 -0
  600. data/po/zh_CN/rbot-uno.po +512 -0
  601. data/po/zh_CN/rbot-urban.po +0 -0
  602. data/po/zh_CN/rbot-url.po +0 -0
  603. data/po/zh_CN/rbot-usermodes.po +0 -0
  604. data/po/zh_CN/rbot-wall.po +33 -0
  605. data/po/zh_CN/rbot-weather.po +0 -0
  606. data/po/zh_CN/rbot-wheelfortune.po +205 -0
  607. data/po/zh_CN/rbot-wow.po +0 -0
  608. data/po/zh_CN/rbot-wserver.po +0 -0
  609. data/po/zh_CN/rbot-youtube.po +58 -0
  610. data/po/zh_CN/rbot.po +1006 -0
  611. data/po/zh_TW/rbot-alias.po +82 -0
  612. data/po/zh_TW/rbot-autoop.po +0 -0
  613. data/po/zh_TW/rbot-autorejoin.po +0 -0
  614. data/po/zh_TW/rbot-azgame.po +186 -0
  615. data/po/zh_TW/rbot-bans.po +0 -0
  616. data/po/zh_TW/rbot-bash.po +0 -0
  617. data/po/zh_TW/rbot-botsnack.po +0 -0
  618. data/po/zh_TW/rbot-cal.po +3 -0
  619. data/po/zh_TW/rbot-chanserv.po +0 -0
  620. data/po/zh_TW/rbot-chucknorris.po +0 -0
  621. data/po/zh_TW/rbot-debugger.po +0 -0
  622. data/po/zh_TW/rbot-deepthoughts.po +0 -0
  623. data/po/zh_TW/rbot-delicious.po +0 -0
  624. data/po/zh_TW/rbot-dice.po +0 -0
  625. data/po/zh_TW/rbot-dict.po +0 -0
  626. data/po/zh_TW/rbot-dictclient.po +111 -0
  627. data/po/zh_TW/rbot-digg.po +0 -0
  628. data/po/zh_TW/rbot-eightball.po +0 -0
  629. data/po/zh_TW/rbot-excuse.po +0 -0
  630. data/po/zh_TW/rbot-factoids.po +107 -0
  631. data/po/zh_TW/rbot-figlet.po +36 -0
  632. data/po/zh_TW/rbot-fish.po +0 -0
  633. data/po/zh_TW/rbot-forecast.po +0 -0
  634. data/po/zh_TW/rbot-fortune.po +0 -0
  635. data/po/zh_TW/rbot-freshmeat.po +0 -0
  636. data/po/zh_TW/rbot-grouphug.po +18 -0
  637. data/po/zh_TW/rbot-hl2.po +0 -0
  638. data/po/zh_TW/rbot-host.po +3 -0
  639. data/po/zh_TW/rbot-imdb.po +0 -0
  640. data/po/zh_TW/rbot-insult.po +0 -0
  641. data/po/zh_TW/rbot-iplookup.po +0 -0
  642. data/po/zh_TW/rbot-karma.po +0 -0
  643. data/po/zh_TW/rbot-keywords.po +24 -0
  644. data/po/zh_TW/rbot-lart.po +0 -0
  645. data/po/zh_TW/rbot-lastfm.po +172 -0
  646. data/po/zh_TW/rbot-linkbot.po +0 -0
  647. data/po/zh_TW/rbot-markov.po +20 -0
  648. data/po/zh_TW/rbot-math.po +0 -0
  649. data/po/zh_TW/rbot-modes.po +0 -0
  650. data/po/zh_TW/rbot-nickrecover.po +36 -0
  651. data/po/zh_TW/rbot-nickserv.po +104 -0
  652. data/po/zh_TW/rbot-nslookup.po +0 -0
  653. data/po/zh_TW/rbot-quakeauth.po +0 -0
  654. data/po/zh_TW/rbot-quiz.po +0 -0
  655. data/po/zh_TW/rbot-quotes.po +108 -0
  656. data/po/zh_TW/rbot-reaction.po +0 -0
  657. data/po/zh_TW/rbot-remind.po +0 -0
  658. data/po/zh_TW/rbot-remotectl.po +0 -0
  659. data/po/zh_TW/rbot-ri.po +0 -0
  660. data/po/zh_TW/rbot-roshambo.po +0 -0
  661. data/po/zh_TW/rbot-rot13.po +0 -0
  662. data/po/zh_TW/rbot-roulette.po +0 -0
  663. data/po/zh_TW/rbot-rss.po +20 -0
  664. data/po/zh_TW/rbot-salut.po +0 -0
  665. data/po/zh_TW/rbot-script.po +0 -0
  666. data/po/zh_TW/rbot-search.po +0 -0
  667. data/po/zh_TW/rbot-seen.po +0 -0
  668. data/po/zh_TW/rbot-shiritori.po +102 -0
  669. data/po/zh_TW/rbot-shortenurls.po +0 -0
  670. data/po/zh_TW/rbot-slashdot.po +0 -0
  671. data/po/zh_TW/rbot-spell.po +54 -0
  672. data/po/zh_TW/rbot-theyfightcrime.po +0 -0
  673. data/po/zh_TW/rbot-threat.po +0 -0
  674. data/po/zh_TW/rbot-time.po +0 -0
  675. data/po/zh_TW/rbot-topic.po +0 -0
  676. data/po/zh_TW/rbot-translator.po +77 -0
  677. data/po/zh_TW/rbot-tube.po +0 -0
  678. data/po/zh_TW/rbot-twitter.po +24 -0
  679. data/po/zh_TW/rbot-uno.po +512 -0
  680. data/po/zh_TW/rbot-urban.po +0 -0
  681. data/po/zh_TW/rbot-url.po +0 -0
  682. data/po/zh_TW/rbot-usermodes.po +0 -0
  683. data/po/zh_TW/rbot-wall.po +33 -0
  684. data/po/zh_TW/rbot-weather.po +0 -0
  685. data/po/zh_TW/rbot-wheelfortune.po +205 -0
  686. data/po/zh_TW/rbot-wow.po +0 -0
  687. data/po/zh_TW/rbot-wserver.po +0 -0
  688. data/po/zh_TW/rbot-youtube.po +58 -0
  689. data/po/zh_TW/rbot.po +1034 -0
  690. data/setup.rb +800 -574
  691. metadata +723 -108
  692. data/data/rbot/contrib/plugins/figlet.rb +0 -20
  693. data/data/rbot/contrib/plugins/ri.rb +0 -83
  694. data/data/rbot/plugins/demauro.rb +0 -95
  695. data/data/rbot/plugins/google.rb +0 -53
  696. data/data/rbot/plugins/opme.rb +0 -19
  697. data/data/rbot/plugins/roshambo.rb +0 -54
  698. data/data/rbot/plugins/rubyurl.rb +0 -39
  699. data/data/rbot/plugins/tinyurl.rb +0 -39
  700. data/data/rbot/plugins/xmlrpc.rb.disabled +0 -52
  701. data/data/rbot/templates/levels.rbot +0 -32
  702. data/data/rbot/templates/users.rbot +0 -1
  703. data/lib/rbot/auth.rb +0 -314
  704. data/lib/rbot/channel.rb +0 -54
  705. data/lib/rbot/httputil.rb +0 -309
  706. data/lib/rbot/utils.rb +0 -83
@@ -0,0 +1,26 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: Usermodes plugin
5
+ #
6
+ # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7
+ # Copyright:: (C) 2007 Giuseppe Bilotta
8
+ # License:: rbot's licence
9
+ #
10
+
11
+ class UserModesPlugin < Plugin
12
+ Config.register Config::StringValue.new('irc.usermodes',
13
+ :default => '',
14
+ :desc => "User modes to set when connecting to the server")
15
+
16
+ def help(plugin, topic="")
17
+ "handles automatic usermode settings on connect. See the config variable irc.usermodes"
18
+ end
19
+
20
+ def connect
21
+ modes = @bot.config['irc.usermodes']
22
+ @bot.mode(@bot.nick, modes, '') unless modes.empty?
23
+ end
24
+ end
25
+
26
+ plugin = UserModesPlugin.new
@@ -0,0 +1,60 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: Wall plugin
5
+ #
6
+ # Author:: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
7
+ # Copyright:: (C) 2008 Giuseppe Bilotta
8
+ # License:: GPLv2
9
+ #
10
+ # Wall plugin: !wall to show the writing on the wall, !wall whatever to write
11
+ # whatever on the wall
12
+ #
13
+ # The wall is global for all rbots and is available at http://ruby-rbot.org/wall/
14
+
15
+ class WallPlugin < Plugin
16
+
17
+ Config.register Config::ArrayValue.new('wall.readers',
18
+ :default => ['toilet', 'figlet'],
19
+ :desc => _('figlet method to use to display the writing on the wall (nil for none)'),
20
+ :validate => Proc.new { |v| v !~ /\s|`/ },
21
+ :on_change => Proc.new { |bot, v| bot.plugins['figlet'].test_toilet })
22
+
23
+ def help(plugin, topic="")
24
+ "wall => read latest writing on the wall: http://ruby-rbot.org/wall"
25
+ end
26
+
27
+ def write_wall(m, p)
28
+ m.reply _("writing on the wall not implemented yet")
29
+ end
30
+
31
+ def read_wall(m, p)
32
+ wall = @bot.httputil.get('http://ruby-rbot.org/wall/', :cache => false)
33
+ if wall.nil_or_empty?
34
+ m.reply _("No writing on the wall")
35
+ return
36
+ end
37
+ if @bot.plugins['figlet']
38
+ reader = nil
39
+ @bot.config['wall.readers'].each { |p|
40
+ plug = p.intern rescue nil
41
+ if @bot.plugins['figlet'].has[plug]
42
+ reader = plug
43
+ break
44
+ end
45
+ }
46
+ if reader
47
+ @bot.plugins['figlet'].figlet m, :plugin => reader,
48
+ :message => wall
49
+ return
50
+ end
51
+ end
52
+ m.reply(_("The writing on the wall reads: %{wall}") % {
53
+ :wall => wall
54
+ })
55
+ end
56
+ end
57
+
58
+ wall = WallPlugin.new
59
+ wall.map 'wall', :action => :read_wall
60
+ wall.map 'wall *text', :action => :write_wall
@@ -1,649 +1,305 @@
1
- # This is nasty-ass. I hate writing parsers.
2
- class Metar
3
- attr_reader :decoded
4
- attr_reader :input
5
- attr_reader :date
6
- attr_reader :nodata
7
- def initialize(string)
8
- str = nil
9
- @nodata = false
10
- string.each_line {|l|
11
- if str == nil
12
- # grab first line (date)
13
- @date = l.chomp.strip
14
- str = ""
15
- else
16
- if(str == "")
17
- str = l.chomp.strip
18
- else
19
- str += " " + l.chomp.strip
20
- end
21
- end
22
- }
23
- if @date && @date =~ /^(\d+)\/(\d+)\/(\d+) (\d+):(\d+)$/
24
- # 2002/02/26 05:00
25
- @date = Time.gm($1, $2, $3, $4, $5, 0)
26
- else
27
- @date = Time.now
28
- end
29
- @input = str.chomp
30
- @cloud_layers = 0
31
- @cloud_coverage = {
32
- 'SKC' => '0',
33
- 'CLR' => '0',
34
- 'VV' => '8/8',
35
- 'FEW' => '1/8 - 2/8',
36
- 'SCT' => '3/8 - 4/8',
37
- 'BKN' => '5/8 - 7/8',
38
- 'OVC' => '8/8'
39
- }
40
- @wind_dir_texts = [
41
- 'North',
42
- 'North/Northeast',
43
- 'Northeast',
44
- 'East/Northeast',
45
- 'East',
46
- 'East/Southeast',
47
- 'Southeast',
48
- 'South/Southeast',
49
- 'South',
50
- 'South/Southwest',
51
- 'Southwest',
52
- 'West/Southwest',
53
- 'West',
54
- 'West/Northwest',
55
- 'Northwest',
56
- 'North/Northwest',
57
- 'North'
58
- ]
59
- @wind_dir_texts_short = [
60
- 'N',
61
- 'N/NE',
62
- 'NE',
63
- 'E/NE',
64
- 'E',
65
- 'E/SE',
66
- 'SE',
67
- 'S/SE',
68
- 'S',
69
- 'S/SW',
70
- 'SW',
71
- 'W/SW',
72
- 'W',
73
- 'W/NW',
74
- 'NW',
75
- 'N/NW',
76
- 'N'
77
- ]
78
- @weather_array = {
79
- 'MI' => 'Mild ',
80
- 'PR' => 'Partial ',
81
- 'BC' => 'Patches ',
82
- 'DR' => 'Low Drifting ',
83
- 'BL' => 'Blowing ',
84
- 'SH' => 'Shower(s) ',
85
- 'TS' => 'Thunderstorm ',
86
- 'FZ' => 'Freezing',
87
- 'DZ' => 'Drizzle ',
88
- 'RA' => 'Rain ',
89
- 'SN' => 'Snow ',
90
- 'SG' => 'Snow Grains ',
91
- 'IC' => 'Ice Crystals ',
92
- 'PE' => 'Ice Pellets ',
93
- 'GR' => 'Hail ',
94
- 'GS' => 'Small Hail and/or Snow Pellets ',
95
- 'UP' => 'Unknown ',
96
- 'BR' => 'Mist ',
97
- 'FG' => 'Fog ',
98
- 'FU' => 'Smoke ',
99
- 'VA' => 'Volcanic Ash ',
100
- 'DU' => 'Widespread Dust ',
101
- 'SA' => 'Sand ',
102
- 'HZ' => 'Haze ',
103
- 'PY' => 'Spray',
104
- 'PO' => 'Well-Developed Dust/Sand Whirls ',
105
- 'SQ' => 'Squalls ',
106
- 'FC' => 'Funnel Cloud Tornado Waterspout ',
107
- 'SS' => 'Sandstorm/Duststorm '
108
- }
109
- @cloud_condition_array = {
110
- 'SKC' => 'clear',
111
- 'CLR' => 'clear',
112
- 'VV' => 'vertical visibility',
113
- 'FEW' => 'a few',
114
- 'SCT' => 'scattered',
115
- 'BKN' => 'broken',
116
- 'OVC' => 'overcast'
117
- }
118
- @strings = {
119
- 'mm_inches' => '%s mm (%s inches)',
120
- 'precip_a_trace' => 'a trace',
121
- 'precip_there_was' => 'There was %s of precipitation ',
122
- 'sky_str_format1' => 'There were %s at a height of %s meters (%s feet)',
123
- 'sky_str_clear' => 'The sky was clear',
124
- 'sky_str_format2' => ', %s at a height of %s meter (%s feet) and %s at a height of %s meters (%s feet)',
125
- 'sky_str_format3' => ' and %s at a height of %s meters (%s feet)',
126
- 'clouds' => ' clouds',
127
- 'clouds_cb' => ' cumulonimbus clouds',
128
- 'clouds_tcu' => ' towering cumulus clouds',
129
- 'visibility_format' => 'The visibility was %s kilometers (%s miles).',
130
- 'wind_str_format1' => 'blowing at a speed of %s meters per second (%s miles per hour)',
131
- 'wind_str_format2' => ', with gusts to %s meters per second (%s miles per hour),',
132
- 'wind_str_format3' => ' from the %s',
133
- 'wind_str_calm' => 'calm',
134
- 'precip_last_hour' => 'in the last hour. ',
135
- 'precip_last_6_hours' => 'in the last 3 to 6 hours. ',
136
- 'precip_last_24_hours' => 'in the last 24 hours. ',
137
- 'precip_snow' => 'There is %s mm (%s inches) of snow on the ground. ',
138
- 'temp_min_max_6_hours' => 'The maximum and minimum temperatures over the last 6 hours were %s and %s degrees Celsius (%s and %s degrees Fahrenheit).',
139
- 'temp_max_6_hours' => 'The maximum temperature over the last 6 hours was %s degrees Celsius (%s degrees Fahrenheit). ',
140
- 'temp_min_6_hours' => 'The minimum temperature over the last 6 hours was %s degrees Celsius (%s degrees Fahrenheit). ',
141
- 'temp_min_max_24_hours' => 'The maximum and minimum temperatures over the last 24 hours were %s and %s degrees Celsius (%s and %s degrees Fahrenheit). ',
142
- 'light' => 'Light ',
143
- 'moderate' => 'Moderate ',
144
- 'heavy' => 'Heavy ',
145
- 'mild' => 'Mild ',
146
- 'nearby' => 'Nearby ',
147
- 'current_weather' => 'Current weather is %s. ',
148
- 'pretty_print_metar' => '%s on %s, the wind was %s at %s. The temperature was %s degrees Celsius (%s degrees Fahrenheit), and the pressure was %s hPa (%s inHg). The relative humidity was %s%%. %s %s %s %s %s'
149
- }
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: Weather plugin for rbot
5
+ #
6
+ # Author:: MrChucho (mrchucho@mrchucho.net): NOAA National Weather Service support
7
+ # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
8
+ #
9
+ # Copyright:: (C) 2006 Ralph M. Churchill
10
+ # Copyright:: (C) 2006-2007 Giuseppe Bilotta
11
+ #
12
+ # License:: GPL v2
150
13
 
151
- parse
152
- end
14
+ require 'rexml/document'
153
15
 
154
- def store_speed(value, windunit, meterspersec, knots, milesperhour)
155
- # Helper function to convert and store speed based on unit.
156
- # &$meterspersec, &$knots and &$milesperhour are passed on
157
- # reference
158
- if (windunit == 'KT')
159
- # The windspeed measured in knots:
160
- @decoded[knots] = sprintf("%.2f", value)
161
- # The windspeed measured in meters per second, rounded to one decimal place:
162
- @decoded[meterspersec] = sprintf("%.2f", value.to_f * 0.51444)
163
- # The windspeed measured in miles per hour, rounded to one decimal place: */
164
- @decoded[milesperhour] = sprintf("%.2f", value.to_f * 1.1507695060844667)
165
- elsif (windunit == 'MPS')
166
- # The windspeed measured in meters per second:
167
- @decoded[meterspersec] = sprintf("%.2f", value)
168
- # The windspeed measured in knots, rounded to one decimal place:
169
- @decoded[knots] = sprintf("%.2f", value.to_f / 0.51444)
170
- #The windspeed measured in miles per hour, rounded to one decimal place:
171
- @decoded[milesperhour] = sprintf("%.1f", value.to_f / 0.51444 * 1.1507695060844667)
172
- elsif (windunit == 'KMH')
173
- # The windspeed measured in kilometers per hour:
174
- @decoded[meterspersec] = sprintf("%.1f", value.to_f * 1000 / 3600)
175
- @decoded[knots] = sprintf("%.1f", value.to_f * 1000 / 3600 / 0.51444)
176
- # The windspeed measured in miles per hour, rounded to one decimal place:
177
- @decoded[milesperhour] = sprintf("%.1f", knots.to_f * 1.1507695060844667)
16
+ # Wraps NOAA National Weather Service information
17
+ class CurrentConditions
18
+ def initialize(station)
19
+ @station = station
20
+ @url = "http://www.nws.noaa.gov/data/current_obs/#{@station.upcase}.xml"
21
+ @etag = String.new
22
+ @mtime = Time.mktime(0)
23
+ @current_conditions = String.new
24
+ @iscached = false
178
25
  end
179
- end
180
-
181
- def parse
182
- @decoded = Hash.new
183
- puts @input
184
- @input.split(" ").each {|part|
185
- if (part == 'METAR')
186
- # Type of Report: METAR
187
- @decoded['type'] = 'METAR'
188
- elsif (part == 'SPECI')
189
- # Type of Report: SPECI
190
- @decoded['type'] = 'SPECI'
191
- elsif (part == 'AUTO')
192
- # Report Modifier: AUTO
193
- @decoded['report_mod'] = 'AUTO'
194
- elsif (part == 'NIL')
195
- @nodata = true
196
- elsif (part =~ /^\S{4}$/ && ! (@decoded.has_key?('station')))
197
- # Station Identifier
198
- @decoded['station'] = part
199
- elsif (part =~ /([0-9]{2})([0-9]{2})([0-9]{2})Z/)
200
- # ignore this bit, it's useless without month/year. some of these
201
- # things are hideously out of date.
202
- # now = Time.new
203
- # time = Time.gm(now.year, now.month, $1, $2, $3, 0)
204
- # Date and Time of Report
205
- # @decoded['time'] = time
206
- elsif (part == 'COR')
207
- # Report Modifier: COR
208
- @decoded['report_mod'] = 'COR'
209
- elsif (part =~ /([0-9]{3}|VRB)([0-9]{2,3}).*(KT|MPS|KMH)/)
210
- # Wind Group
211
- windunit = $3
212
- # now do ereg to get the actual values
213
- part =~ /([0-9]{3}|VRB)([0-9]{2,3})((G[0-9]{2,3})?#{windunit})/
214
- if ($1 == 'VRB')
215
- @decoded['wind_deg'] = 'variable directions'
216
- @decoded['wind_dir_text'] = 'variable directions'
217
- @decoded['wind_dir_text_short'] = 'VAR'
218
- else
219
- @decoded['wind_deg'] = $1
220
- @decoded['wind_dir_text'] = @wind_dir_texts[($1.to_i/22.5).round]
221
- @decoded['wind_dir_text_short'] = @wind_dir_texts_short[($1.to_i/22.5).round]
26
+ def update
27
+ begin
28
+ open(@url,"If-Modified-Since" => @mtime.rfc2822) do |feed|
29
+ # open(@url,"If-None-Match"=>@etag) do |feed|
30
+ @etag = feed.meta['etag']
31
+ @mtime = feed.last_modified
32
+ cc_doc = (REXML::Document.new feed).root
33
+ @iscached = false
34
+ @current_conditions = parse(cc_doc)
35
+ end
36
+ rescue OpenURI::HTTPError => e
37
+ case e
38
+ when /304/:
39
+ @iscached = true
40
+ when /404/:
41
+ raise "Data for #{@station} not found"
42
+ else
43
+ raise "Error retrieving data: #{e}"
44
+ end
222
45
  end
223
- store_speed($2, windunit,
224
- 'wind_meters_per_second',
225
- 'wind_knots',
226
- 'wind_miles_per_hour')
227
-
228
- if ($4 != nil)
229
- # We have a report with information about the gust.
230
- # First we have the gust measured in knots
231
- if ($4 =~ /G([0-9]{2,3})/)
232
- store_speed($1,windunit,
233
- 'wind_gust_meters_per_second',
234
- 'wind_gust_knots',
235
- 'wind_gust_miles_per_hour')
46
+ @current_conditions # +" Cached? "+ ((@iscached) ? "Y" : "N")
236
47
  end
48
+ def parse(cc_doc)
49
+ cc = Hash.new
50
+ cc_doc.elements.each do |c|
51
+ cc[c.name.to_sym] = c.text
237
52
  end
238
- elsif (part =~ /([0-9]{3})V([0-9]{3})/)
239
- # Variable wind-direction
240
- @decoded['wind_var_beg'] = $1
241
- @decoded['wind_var_end'] = $2
242
- elsif (part == "9999")
243
- # A strange value. When you look at other pages you see it
244
- # interpreted like this (where I use > to signify 'Greater
245
- # than'):
246
- @decoded['visibility_miles'] = '>7';
247
- @decoded['visibility_km'] = '>11.3';
248
- elsif (part =~ /^([0-9]{4})$/)
249
- # Visibility in meters (4 digits only)
250
- # The visibility measured in kilometers, rounded to one decimal place.
251
- @decoded['visibility_km'] = sprintf("%.1f", $1.to_i / 1000)
252
- # The visibility measured in miles, rounded to one decimal place.
253
- @decoded['visibility_miles'] = sprintf("%.1f", $1.to_i / 1000 / 1.609344)
254
- elsif (part =~ /^[0-9]$/)
255
- # Temp Visibility Group, single digit followed by space
256
- @decoded['temp_visibility_miles'] = part
257
- elsif (@decoded['temp_visibility_miles'] && (@decoded['temp_visibility_miles']+' '+part) =~ /^M?(([0-9]?)[ ]?([0-9])(\/?)([0-9]*))SM$/)
258
- # Visibility Group
259
- if ($4 == '/')
260
- vis_miles = $2.to_i + $3.to_i/$5.to_i
261
- else
262
- vis_miles = $1.to_i;
263
- end
264
- if (@decoded['temp_visibility_miles'][0] == 'M')
265
- # The visibility measured in miles, prefixed with < to indicate 'Less than'
266
- @decoded['visibility_miles'] = '<' + sprintf("%.1f", vis_miles)
267
- # The visibility measured in kilometers. The value is rounded
268
- # to one decimal place, prefixed with < to indicate 'Less than' */
269
- @decoded['visibility_km'] = '<' . sprintf("%.1f", vis_miles * 1.609344)
270
- else
271
- # The visibility measured in mile.s */
272
- @decoded['visibility_miles'] = sprintf("%.1f", vis_miles)
273
- # The visibility measured in kilometers, rounded to one decimal place.
274
- @decoded['visibility_km'] = sprintf("%.1f", vis_miles * 1.609344)
275
- end
276
- elsif (part =~ /^(-|\+|VC|MI)?(TS|SH|FZ|BL|DR|BC|PR|RA|DZ|SN|SG|GR|GS|PE|IC|UP|BR|FG|FU|VA|DU|SA|HZ|PY|PO|SQ|FC|SS|DS)+$/)
277
- # Current weather-group
278
- @decoded['weather'] = '' unless @decoded.has_key?('weather')
279
- if (part[0].chr == '-')
280
- # A light phenomenon
281
- @decoded['weather'] += @strings['light']
282
- part = part[1,part.length]
283
- elsif (part[0].chr == '+')
284
- # A heavy phenomenon
285
- @decoded['weather'] += @strings['heavy']
286
- part = part[1,part.length]
287
- elsif (part[0,2] == 'VC')
288
- # Proximity Qualifier
289
- @decoded['weather'] += @strings['nearby']
290
- part = part[2,part.length]
291
- elsif (part[0,2] == 'MI')
292
- @decoded['weather'] += @strings['mild']
293
- part = part[2,part.length]
294
- else
295
- # no intensity code => moderate phenomenon
296
- @decoded['weather'] += @strings['moderate']
297
- end
298
-
299
- while (part && bite = part[0,2]) do
300
- # Now we take the first two letters and determine what they
301
- # mean. We append this to the variable so that we gradually
302
- # build up a phrase.
303
-
304
- @decoded['weather'] += @weather_array[bite]
305
- # Here we chop off the two first letters, so that we can take
306
- # a new bite at top of the while-loop.
307
- part = part[2,-1]
308
- end
309
- elsif (part =~ /(SKC|CLR)/)
310
- # Cloud-layer-group.
311
- # There can be up to three of these groups, so we store them as
312
- # cloud_layer1, cloud_layer2 and cloud_layer3.
313
-
314
- @cloud_layers += 1;
315
- # Again we have to translate the code-characters to a
316
- # meaningful string.
317
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_condition'] = @cloud_condition_array[$1]
318
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_coverage'] = @cloud_coverage[$1]
319
- elsif (part =~ /^(VV|FEW|SCT|BKN|OVC)([0-9]{3})(CB|TCU)?$/)
320
- # We have found (another) a cloud-layer-group. There can be up
321
- # to three of these groups, so we store them as cloud_layer1,
322
- # cloud_layer2 and cloud_layer3.
323
- @cloud_layers += 1;
324
- # Again we have to translate the code-characters to a meaningful string.
325
- if ($3 == 'CB')
326
- # cumulonimbus (CB) clouds were observed. */
327
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_condition'] =
328
- @cloud_condition_array[$1] + @strings['clouds_cb']
329
- elsif ($3 == 'TCU')
330
- # towering cumulus (TCU) clouds were observed.
331
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_condition'] =
332
- @cloud_condition_array[$1] + @strings['clouds_tcu']
333
- else
334
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_condition'] =
335
- @cloud_condition_array[$1] + @strings['clouds']
336
- end
337
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_coverage'] = @cloud_coverage[$1]
338
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_altitude_ft'] = $2.to_i * 100
339
- @decoded['cloud_layer'+ (@cloud_layers.to_s) +'_altitude_m'] = ($2.to_f * 30.48).round
340
- elsif (part =~ /^T([0-9]{4})$/)
341
- store_temp($1,'temp_c','temp_f')
342
- elsif (part =~ /^T?(M?[0-9]{2})\/(M?[0-9\/]{1,2})?$/)
343
- # Temperature/Dew Point Group
344
- # The temperature and dew-point measured in Celsius.
345
- @decoded['temp_c'] = sprintf("%d", $1.tr('M', '-'))
346
- if $2 == "//" || !$2
347
- @decoded['dew_c'] = 0
53
+ "At #{cc[:observation_time_rfc822]}, the wind was #{cc[:wind_string]} at #{cc[:location]} (#{cc[:station_id]}). The temperature was #{cc[:temperature_string]}#{heat_index_or_wind_chill(cc)}, and the pressure was #{cc[:pressure_string]}. The relative humidity was #{cc[:relative_humidity]}%. Current conditions are #{cc[:weather]} with #{cc[:visibility_mi]}mi visibility."
54
+ end
55
+ private
56
+ def heat_index_or_wind_chill(cc)
57
+ hi = cc[:heat_index_string]
58
+ wc = cc[:windchill_string]
59
+ if hi != 'NA' then
60
+ " with a heat index of #{hi}"
61
+ elsif wc != 'NA' then
62
+ " with a windchill of #{wc}"
348
63
  else
349
- @decoded['dew_c'] = sprintf("%.1f", $2.tr('M', '-'))
64
+ ""
350
65
  end
351
- # The temperature and dew-point measured in Fahrenheit, rounded to
352
- # the nearest degree.
353
- @decoded['temp_f'] = ((@decoded['temp_c'].to_f * 9 / 5) + 32).round
354
- @decoded['dew_f'] = ((@decoded['dew_c'].to_f * 9 / 5) + 32).round
355
- elsif(part =~ /A([0-9]{4})/)
356
- # Altimeter
357
- # The pressure measured in inHg
358
- @decoded['altimeter_inhg'] = sprintf("%.2f", $1.to_i/100)
359
- # The pressure measured in mmHg, hPa and atm
360
- @decoded['altimeter_mmhg'] = sprintf("%.1f", $1.to_f * 0.254)
361
- @decoded['altimeter_hpa'] = sprintf("%d", ($1.to_f * 0.33863881578947).to_i)
362
- @decoded['altimeter_atm'] = sprintf("%.3f", $1.to_f * 3.3421052631579e-4)
363
- elsif(part =~ /Q([0-9]{4})/)
364
- # Altimeter
365
- # This is strange, the specification doesnt say anything about
366
- # the Qxxxx-form, but it's in the METARs.
367
- # The pressure measured in hPa
368
- @decoded['altimeter_hpa'] = sprintf("%d", $1.to_i)
369
- # The pressure measured in mmHg, inHg and atm
370
- @decoded['altimeter_mmhg'] = sprintf("%.1f", $1.to_f * 0.7500616827)
371
- @decoded['altimeter_inhg'] = sprintf("%.2f", $1.to_f * 0.0295299875)
372
- @decoded['altimeter_atm'] = sprintf("%.3f", $1.to_f * 9.869232667e-4)
373
- elsif (part =~ /^T([0-9]{4})([0-9]{4})/)
374
- # Temperature/Dew Point Group, coded to tenth of degree.
375
- # The temperature and dew-point measured in Celsius.
376
- store_temp($1,'temp_c','temp_f')
377
- store_temp($2,'dew_c','dew_f')
378
- elsif (part =~ /^1([0-9]{4}$)/)
379
- # 6 hour maximum temperature Celsius, coded to tenth of degree
380
- store_temp($1,'temp_max6h_c','temp_max6h_f')
381
- elsif (part =~ /^2([0-9]{4}$)/)
382
- # 6 hour minimum temperature Celsius, coded to tenth of degree
383
- store_temp($1,'temp_min6h_c','temp_min6h_f')
384
- elsif (part =~ /^4([0-9]{4})([0-9]{4})$/)
385
- # 24 hour maximum and minimum temperature Celsius, coded to
386
- # tenth of degree
387
- store_temp($1,'temp_max24h_c','temp_max24h_f')
388
- store_temp($2,'temp_min24h_c','temp_min24h_f')
389
- elsif (part =~ /^P([0-9]{4})/)
390
- # Precipitation during last hour in hundredths of an inch
391
- # (store as inches)
392
- @decoded['precip_in'] = sprintf("%.2f", $1.to_f/100)
393
- @decoded['precip_mm'] = sprintf("%.2f", $1.to_f * 0.254)
394
- elsif (part =~ /^6([0-9]{4})/)
395
- # Precipitation during last 3 or 6 hours in hundredths of an
396
- # inch (store as inches)
397
- @decoded['precip_6h_in'] = sprintf("%.2f", $1.to_f/100)
398
- @decoded['precip_6h_mm'] = sprintf("%.2f", $1.to_f * 0.254)
399
- elsif (part =~ /^7([0-9]{4})/)
400
- # Precipitation during last 24 hours in hundredths of an inch
401
- # (store as inches)
402
- @decoded['precip_24h_in'] = sprintf("%.2f", $1.to_f/100)
403
- @decoded['precip_24h_mm'] = sprintf("%.2f", $1.to_f * 0.254)
404
- elsif(part =~ /^4\/([0-9]{3})/)
405
- # Snow depth in inches
406
- @decoded['snow_in'] = sprintf("%.2f", $1);
407
- @decoded['snow_mm'] = sprintf("%.2f", $1.to_f * 25.4)
408
- else
409
- # If we couldn't match the group, we assume that it was a
410
- # remark.
411
- @decoded['remarks'] = '' unless @decoded.has_key?("remarks")
412
- @decoded['remarks'] += ' ' + part;
413
- end
414
- }
415
-
416
- # Relative humidity
417
- # p @decoded['dew_c'] # 11.0
418
- # p @decoded['temp_c'] # 21.0
419
- # => 56.1
420
- @decoded['rel_humidity'] = sprintf("%.1f",100 *
421
- (6.11 * (10.0**(7.5 * @decoded['dew_c'].to_f / (237.7 + @decoded['dew_c'].to_f)))) / (6.11 * (10.0 ** (7.5 * @decoded['temp_c'].to_f / (237.7 + @decoded['temp_c'].to_f))))) if @decoded.has_key?('dew_c')
422
- end
66
+ end
67
+ end
423
68
 
424
- def store_temp(temp,temp_cname,temp_fname)
425
- # Given a numerical temperature temp in Celsius, coded to tenth of
426
- # degree, store in @decoded[temp_cname], convert to Fahrenheit
427
- # and store in @decoded[temp_fname]
428
- # Note: temp is converted to negative if temp > 100.0 (See
429
- # Federal Meteorological Handbook for groups T, 1, 2 and 4)
69
+ class WeatherPlugin < Plugin
430
70
 
431
- # Temperature measured in Celsius, coded to tenth of degree
432
- temp = temp.to_f/10
433
- if (temp >100.0)
434
- # first digit = 1 means minus temperature
435
- temp = -(temp - 100.0)
71
+ Config.register Config::BooleanValue.new('weather.advisory',
72
+ :default => true,
73
+ :desc => "Should the bot report special weather advisories when any is present?")
74
+
75
+ def help(plugin, topic="")
76
+ case topic
77
+ when "nws"
78
+ "weather nws <station> => display the current conditions at the location specified by the NOAA National Weather Service station code <station> ( lookup your station code at http://www.nws.noaa.gov/data/current_obs/ )"
79
+ when "station", "wu"
80
+ "weather [<units>] <location> => display the current conditions at the location specified, looking it up on the Weather Underground site; you can use 'station <code>' to look up data by station code ( lookup your station code at http://www.weatherunderground.com/ ); you can optionally set <units> to 'metric' or 'english' if you only want data with the units; use 'both' for units to go back to having both."
81
+ else
82
+ "weather information lookup. Looks up weather information for the last location you specified. See topics 'nws' and 'wu' for more information"
436
83
  end
437
- @decoded[temp_cname] = sprintf("%.1f", temp)
438
- # The temperature in Fahrenheit.
439
- @decoded[temp_fname] = sprintf("%.1f", (temp * 9 / 5) + 32)
440
84
  end
85
+
86
+ def initialize
87
+ super
441
88
 
442
- def pretty_print_precip(precip_mm, precip_in)
443
- # Returns amount if $precip_mm > 0, otherwise "trace" (see Federal
444
- # Meteorological Handbook No. 1 for code groups P, 6 and 7) used in
445
- # several places, so standardized in one function.
446
- if (precip_mm.to_i > 0)
447
- amount = sprintf(@strings['mm_inches'], precip_mm, precip_in)
448
- else
449
- amount = @strings['a_trace']
450
- end
451
- return sprintf(@strings['precip_there_was'], amount)
452
- end
89
+ @nws_cache = Hash.new
453
90
 
454
- def pretty_print
455
- if @nodata
456
- return "The weather stored for #{@decoded['station']} consists of the string 'NIL' :("
457
- end
91
+ @wu_url = "http://mobile.wunderground.com/cgi-bin/findweather/getForecast?brand=mobile%s&query=%s"
92
+ @wu_station_url = "http://mobile.wunderground.com/auto/mobile%s/global/stations/%s.html"
93
+ end
94
+
95
+ def weather(m, params)
96
+ if params[:where].empty?
97
+ if @registry.has_key?(m.sourcenick)
98
+ where = @registry[m.sourcenick]
99
+ debug "Loaded weather info #{where.inspect} for #{m.sourcenick}"
458
100
 
459
- ["temp_c", "altimeter_hpa"].each {|key|
460
- if !@decoded.has_key?(key)
461
- return "The weather stored for #{@decoded['station']} could not be parsed (#{@input})"
101
+ service = where.first.to_sym
102
+ loc = where[1].to_s
103
+ units = params[:units] || where[2] rescue nil
104
+ else
105
+ debug "No weather info for #{m.sourcenick}"
106
+ m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
107
+ return
462
108
  end
463
- }
464
-
465
- mins_old = ((Time.now - @date.to_i).to_f/60).round
466
- if (mins_old <= 60)
467
- weather_age = mins_old.to_s + " minutes ago,"
468
- elsif (mins_old <= 60 * 25)
469
- weather_age = (mins_old / 60).to_s + " hours, "
470
- weather_age += (mins_old % 60).to_s + " minutes ago,"
471
- else
472
- # return "The weather stored for #{@decoded['station']} is hideously out of date :( (Last update #{@date})"
473
- weather_age = "The weather stored for #{@decoded['station']} is hideously out of date :( here it is anyway:"
474
- end
475
-
476
- if(@decoded.has_key?("cloud_layer1_altitude_ft"))
477
- sky_str = sprintf(@strings['sky_str_format1'],
478
- @decoded["cloud_layer1_condition"],
479
- @decoded["cloud_layer1_altitude_m"],
480
- @decoded["cloud_layer1_altitude_ft"])
481
109
  else
482
- sky_str = @strings['sky_str_clear']
483
- end
484
-
485
- if(@decoded.has_key?("cloud_layer2_altitude_ft"))
486
- if(@decoded.has_key?("cloud_layer3_altitude_ft"))
487
- sky_str += sprintf(@strings['sky_str_format2'],
488
- @decoded["cloud_layer2_condition"],
489
- @decoded["cloud_layer2_altitude_m"],
490
- @decoded["cloud_layer2_altitude_ft"],
491
- @decoded["cloud_layer3_condition"],
492
- @decoded["cloud_layer3_altitude_m"],
493
- @decoded["cloud_layer3_altitude_ft"])
110
+ where = params[:where]
111
+ if ['nws','station'].include?(where.first)
112
+ service = where.first.to_sym
113
+ loc = where[1].to_s
494
114
  else
495
- sky_str += sprintf(@strings['sky_str_format3'],
496
- @decoded["cloud_layer2_condition"],
497
- @decoded["cloud_layer2_altitude_m"],
498
- @decoded["cloud_layer2_altitude_ft"])
115
+ service = :wu
116
+ loc = where.to_s
499
117
  end
118
+ units = params[:units]
500
119
  end
501
- sky_str += "."
502
120
 
503
- if(@decoded.has_key?("visibility_miles"))
504
- visibility = sprintf(@strings['visibility_format'],
505
- @decoded["visibility_km"],
506
- @decoded["visibility_miles"])
507
- else
508
- visibility = ""
121
+ if loc.empty?
122
+ debug "No weather location found for #{m.sourcenick}"
123
+ m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
124
+ return
509
125
  end
510
126
 
511
- if (@decoded.has_key?("wind_meters_per_second") && @decoded["wind_meters_per_second"].to_i > 0)
512
- wind_str = sprintf(@strings['wind_str_format1'],
513
- @decoded["wind_meters_per_second"],
514
- @decoded["wind_miles_per_hour"])
515
- if (@decoded.has_key?("wind_gust_meters_per_second") && @decoded["wind_gust_meters_per_second"].to_i > 0)
516
- wind_str += sprintf(@strings['wind_str_format2'],
517
- @decoded["wind_gust_meters_per_second"],
518
- @decoded["wind_gust_miles_per_hour"])
127
+ wu_units = String.new
128
+ if units
129
+ case units.to_sym
130
+ when :english, :metric
131
+ wu_units = "_#{units}"
132
+ when :both
133
+ else
134
+ m.reply "Ignoring unknown units #{units}"
135
+ wu_units = String.new
519
136
  end
520
- wind_str += sprintf(@strings['wind_str_format3'],
521
- @decoded["wind_dir_text"])
522
- else
523
- wind_str = @strings['wind_str_calm']
524
137
  end
525
138
 
526
- prec_str = ""
527
- if (@decoded.has_key?("precip_in"))
528
- prec_str += pretty_print_precip(@decoded["precip_mm"], @decoded["precip_in"]) + @strings['precip_last_hour']
529
- end
530
- if (@decoded.has_key?("precip_6h_in"))
531
- prec_str += pretty_print_precip(@decoded["precip_6h_mm"], @decoded["precip_6h_in"]) + @strings['precip_last_6_hours']
532
- end
533
- if (@decoded.has_key?("precip_24h_in"))
534
- prec_str += pretty_print_precip(@decoded["precip_24h_mm"], @decoded["precip_24h_in"]) + @strings['precip_last_24_hours']
535
- end
536
- if (@decoded.has_key?("snow_in"))
537
- prec_str += sprintf(@strings['precip_snow'], @decoded["snow_mm"], @decoded["snow_in"])
139
+ case service
140
+ when :nws
141
+ nws_describe(m, loc)
142
+ when :station
143
+ wu_station(m, loc, wu_units)
144
+ when :wu
145
+ wu_weather(m, loc, wu_units)
538
146
  end
539
147
 
540
- temp_str = ""
541
- if (@decoded.has_key?("temp_max6h_c") && @decoded.has_key?("temp_min6h_c"))
542
- temp_str += sprintf(@strings['temp_min_max_6_hours'],
543
- @decoded["temp_max6h_c"],
544
- @decoded["temp_min6h_c"],
545
- @decoded["temp_max6h_f"],
546
- @decoded["temp_min6h_f"])
148
+ @registry[m.sourcenick] = [service, loc, units]
149
+ end
150
+
151
+ def nws_describe(m, where)
152
+ if @nws_cache.has_key?(where) then
153
+ met = @nws_cache[where]
547
154
  else
548
- if (@decoded.has_key?("temp_max6h_c"))
549
- temp_str += sprintf(@strings['temp_max_6_hours'],
550
- @decoded["temp_max6h_c"],
551
- @decoded["temp_max6h_f"])
552
- end
553
- if (@decoded.has_key?("temp_min6h_c"))
554
- temp_str += sprintf(@strings['temp_max_6_hours'],
555
- @decoded["temp_min6h_c"],
556
- @decoded["temp_min6h_f"])
557
- end
558
- end
559
- if (@decoded.has_key?("temp_max24h_c"))
560
- temp_str += sprintf(@strings['temp_min_max_24_hours'],
561
- @decoded["temp_max24h_c"],
562
- @decoded["temp_min24h_c"],
563
- @decoded["temp_max24h_f"],
564
- @decoded["temp_min24h_f"])
155
+ met = CurrentConditions.new(where)
565
156
  end
566
-
567
- if (@decoded.has_key?("weather"))
568
- weather_str = sprintf(@strings['current_weather'], @decoded["weather"])
157
+ if met
158
+ begin
159
+ m.reply met.update
160
+ @nws_cache[where] = met
161
+ rescue => e
162
+ m.reply e.message
163
+ end
569
164
  else
570
- weather_str = ''
165
+ m.reply "couldn't find weather data for #{where}"
571
166
  end
572
-
573
- return sprintf(@strings['pretty_print_metar'],
574
- weather_age,
575
- @date,
576
- wind_str, @decoded["station"], @decoded["temp_c"],
577
- @decoded["temp_f"], @decoded["altimeter_hpa"],
578
- @decoded["altimeter_inhg"],
579
- @decoded["rel_humidity"], sky_str,
580
- visibility, weather_str, prec_str, temp_str).strip
581
167
  end
582
168
 
583
- def to_s
584
- @input
169
+ def wu_station(m, where, units)
170
+ begin
171
+ xml = @bot.httputil.get(@wu_station_url % [units, CGI.escape(where)])
172
+ case xml
173
+ when nil
174
+ m.reply "couldn't retrieve weather information, sorry"
175
+ return
176
+ when /Search not found:/
177
+ m.reply "no such station found (#{where})"
178
+ return
179
+ when /<table border.*?>(.*?)<\/table>/m
180
+ data = $1.dup
181
+ m.reply wu_weather_filter(data)
182
+ wu_out_special(m, xml)
183
+ else
184
+ debug xml
185
+ m.reply "something went wrong with the data for #{where}..."
186
+ end
187
+ rescue => e
188
+ m.reply "retrieving info about '#{where}' failed (#{e})"
189
+ end
585
190
  end
586
- end
587
-
588
191
 
589
- class WeatherPlugin < Plugin
590
-
591
- def help(plugin, topic="")
592
- "weather <ICAO> => display the current weather at the location specified by the ICAO code [Lookup your ICAO code at http://www.nws.noaa.gov/tg/siteloc.shtml - this will also store the ICAO against your nick, so you can later just say \"weather\", weather => display the current weather at the location you last asked for"
192
+ def wu_weather(m, where, units)
193
+ begin
194
+ xml = @bot.httputil.get(@wu_url % [units, CGI.escape(where)])
195
+ case xml
196
+ when nil
197
+ m.reply "couldn't retrieve weather information, sorry"
198
+ when /City Not Found/
199
+ m.reply "no such location found (#{where})"
200
+ when /Current<\/a>/
201
+ data = ""
202
+ xml.scan(/<table border.*?>(.*?)<\/table>/m).each do |match|
203
+ data += wu_weather_filter(match.first)
204
+ end
205
+ if data.length > 0
206
+ m.reply data
207
+ else
208
+ m.reply "couldn't parse weather data from #{where}"
209
+ end
210
+ wu_out_special(m, xml)
211
+ when /<a href="\/(?:global\/stations|US\/\w\w)\//
212
+ wu_weather_multi(m, xml)
213
+ else
214
+ debug xml
215
+ m.reply "something went wrong with the data from #{where}..."
216
+ end
217
+ rescue => e
218
+ m.reply "retrieving info about '#{where}' failed (#{e})"
219
+ end
593
220
  end
594
221
 
595
- def get_metar(station)
596
- station.upcase!
597
-
598
- result = @bot.httputil.get(URI.parse("http://weather.noaa.gov/pub/data/observations/metar/stations/#{station}.TXT"))
599
- return nil unless result
600
- return Metar.new(result)
222
+ def wu_clean(stuff)
223
+ txt = stuff
224
+ txt.gsub!(/[\n\s]+/,' ')
225
+ txt.gsub!(/&nbsp;/, ' ')
226
+ txt.gsub!(/&#176;/, ' ') # degree sign
227
+ txt.gsub!(/<\/?b>/,'')
228
+ txt.gsub!(/<\/?span[^<>]*?>/,'')
229
+ txt.gsub!(/<img\s*[^<>]*?>/,'')
230
+ txt.gsub!(/<br\s?\/?>/,'')
231
+ txt
601
232
  end
602
233
 
603
-
604
- def initialize
605
- super
606
- # this plugin only wants to store strings
607
- class << @registry
608
- def store(val)
609
- val
610
- end
611
- def restore(val)
612
- val
234
+ def wu_weather_multi(m, xml)
235
+ # debug xml
236
+ stations = xml.scan(/<td>\s*(?:<a href="([^?"]+\?feature=[^"]+)"\s*[^>]*><img [^>]+><\/a>\s*)?<a href="\/(?:global\/stations|US\/(\w\w))\/([^"]*?)\.html">(.*?)<\/a>\s*:\s*(.*?)<\/td>/m)
237
+ # debug stations
238
+ m.reply "multiple stations available, use 'weather station <code>' or 'weather <city, state>' as appropriate, for one of the following (current temp shown):"
239
+ stations.map! { |ar|
240
+ warning = ar[0]
241
+ loc = ar[2]
242
+ state = ar[1]
243
+ par = ar[3]
244
+ w = ar[4]
245
+ if state # US station
246
+ (warning ? "*" : "") + ("%s, %s (%s): %s" % [loc, state, par, wu_clean(w)])
247
+ else # non-US station
248
+ (warning ? "*" : "") + ("station %s (%s): %s" % [loc, par, wu_clean(w)])
613
249
  end
614
- end
615
- @metar_cache = Hash.new
250
+ }
251
+ m.reply stations.join("; ")
616
252
  end
617
-
618
- def describe(m, where)
619
- if @metar_cache.has_key?(where) &&
620
- Time.now - @metar_cache[where].date < 3600
621
- met = @metar_cache[where]
622
- else
623
- met = get_metar(where)
624
- end
625
-
626
- if met
627
- m.reply met.pretty_print
628
- @metar_cache[where] = met
629
- else
630
- m.reply "couldn't find weather data for #{where}"
253
+
254
+ def wu_check_special(xml)
255
+ specials = []
256
+ # We only scan the first half to prevent getting the advisories twice
257
+ xml[0,xml.length/2].scan(%r{<a href="([^"]+\?[^"]*feature=warning#([^"]+))"[^>]*>([^<]+)</a>}) do
258
+ special = {
259
+ :url => "http://mobile.wunderground.com"+$1,
260
+ :type => $2.dup,
261
+ :special => $3.dup
262
+ }
263
+ spec_rx = Regexp.new("<a name=\"#{special[:type]}\">(?:.+?)<td align=\"left\">\\s+(.+?)\\s+</td>\\s+</tr>\\s+</table>", Regexp::MULTILINE)
264
+ spec_xml = @bot.httputil.get(special[:url])
265
+ if spec_xml and spec_td = spec_xml.match(spec_rx)
266
+ special.merge!(:text => spec_td.captures.first.ircify_html)
267
+ end
268
+ specials << special
631
269
  end
270
+ return specials
632
271
  end
633
272
 
634
- def weather(m, params)
635
- if params[:where]
636
- @registry[m.sourcenick] = params[:where]
637
- describe(m,params[:where])
638
- else
639
- if @registry.has_key?(m.sourcenick)
640
- where = @registry[m.sourcenick]
641
- describe(m,where)
273
+ def wu_out_special(m, xml)
274
+ return unless @bot.config['weather.advisory']
275
+ specials = wu_check_special(xml)
276
+ debug specials
277
+ specials.each do |special|
278
+ special.merge!(:underline => Underline)
279
+ if special[:text]
280
+ m.reply("%{underline}%{special}%{underline}: %{text}" % special)
642
281
  else
643
- m.reply "I don't know where you are yet! Lookup your code at http://www.nws.noaa.gov/tg/siteloc.shtml and tell me 'weather <code>', then I'll know."
282
+ m.reply("%{underline}%{special}%{underline} @ %{url}" % special)
644
283
  end
645
284
  end
646
285
  end
286
+
287
+ def wu_weather_filter(stuff)
288
+ txt = wu_clean(stuff)
289
+
290
+ result = Array.new
291
+ if txt.match(/<\/a>\s*Updated:\s*(.*?)\s*Observed at\s*(.*?)\s*<\/td>/)
292
+ result << ("Weather info for %s (updated on %s)" % [$2, $1])
293
+ end
294
+ txt.scan(/<tr>\s*<td>\s*(.*?)\s*<\/td>\s*<td>\s*(.*?)\s*<\/td>\s*<\/tr>/) { |k, v|
295
+ next if v.empty?
296
+ next if ["-", "- approx.", "N/A", "N/A approx."].include?(v)
297
+ next if k == "Raw METAR"
298
+ result << ("%s: %s" % [k, v])
299
+ }
300
+ return result.join('; ')
301
+ end
647
302
  end
303
+
648
304
  plugin = WeatherPlugin.new
649
- plugin.map 'weather :where', :defaults => {:where => false}
305
+ plugin.map 'weather :units *where', :defaults => {:where => false, :units => false}, :requirements => {:units => /metric|english|both/}