rbot 0.9.14 → 0.9.15
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +6 -2
- data/REQUIREMENTS +7 -1
- data/Rakefile +10 -32
- data/bin/rbot +6 -1
- data/bin/svnwatch-postcommit-hook +68 -0
- data/data/rbot/contrib/plugins/stats.rb +3 -3
- data/data/rbot/contrib/plugins/vandale.rb +1 -1
- data/data/rbot/filters/rss.rb +72 -0
- data/data/rbot/languages/finnish.lang +50 -0
- data/data/rbot/plugins/alias.rb +6 -6
- data/data/rbot/plugins/autorejoin.rb +41 -2
- data/data/rbot/plugins/bans.rb +100 -6
- data/data/rbot/plugins/bash.rb +9 -4
- data/data/rbot/plugins/cal.rb +1 -1
- data/data/rbot/plugins/chucknorris.rb +6 -6
- data/data/rbot/plugins/debugger.rb +7 -3
- data/data/rbot/plugins/deepthoughts.rb +1 -1
- data/data/rbot/plugins/delicious.rb +6 -2
- data/data/rbot/plugins/dice.rb +7 -7
- data/data/rbot/plugins/dict.rb +4 -3
- data/data/rbot/plugins/dictclient.rb +17 -13
- data/data/rbot/plugins/digg.rb +3 -3
- data/data/rbot/plugins/eightball.rb +1 -1
- data/data/rbot/plugins/factoids.rb +13 -4
- data/data/rbot/plugins/figlet.rb +4 -4
- data/data/rbot/plugins/forecast.rb +3 -3
- data/data/rbot/plugins/fortune.rb +14 -8
- data/data/rbot/plugins/freshmeat.rb +2 -2
- data/data/rbot/plugins/games/azgame.rb +72 -19
- data/data/rbot/plugins/games/hangman.rb +499 -0
- data/data/rbot/plugins/games/quiz.rb +15 -13
- data/data/rbot/plugins/games/roshambo.rb +1 -1
- data/data/rbot/plugins/games/roulette.rb +4 -4
- data/data/rbot/plugins/games/shiritori.rb +31 -31
- data/data/rbot/plugins/games/uno.rb +28 -6
- data/data/rbot/plugins/games/wheelfortune.rb +1 -3
- data/data/rbot/plugins/geoip.rb +83 -28
- data/data/rbot/plugins/googlefight.rb +64 -0
- data/data/rbot/plugins/greet.rb +45 -0
- data/data/rbot/plugins/grouphug.rb +40 -12
- data/data/rbot/plugins/imdb.rb +4 -4
- data/data/rbot/plugins/insult.rb +2 -2
- data/data/rbot/plugins/karma.rb +6 -5
- data/data/rbot/plugins/keywords.rb +26 -22
- data/data/rbot/plugins/lart.rb +5 -6
- data/data/rbot/plugins/lastfm.rb +488 -125
- data/data/rbot/plugins/lib_spotify.rb +84 -0
- data/data/rbot/plugins/linkbot.rb +1 -1
- data/data/rbot/plugins/markov.rb +567 -78
- data/data/rbot/plugins/math.rb +3 -3
- data/data/rbot/plugins/modes.rb +1 -1
- data/data/rbot/plugins/nickrecover.rb +1 -1
- data/data/rbot/plugins/nickserv.rb +7 -7
- data/data/rbot/plugins/note.rb +55 -0
- data/data/rbot/plugins/nslookup.rb +2 -2
- data/data/rbot/plugins/quakeauth.rb +4 -4
- data/data/rbot/plugins/quotes.rb +53 -19
- data/data/rbot/plugins/reaction.rb +76 -19
- data/data/rbot/plugins/remind.rb +3 -96
- data/data/rbot/plugins/ri.rb +1 -1
- data/data/rbot/plugins/rot13.rb +1 -1
- data/data/rbot/plugins/rss.rb +296 -190
- data/data/rbot/plugins/salut.rb +8 -8
- data/data/rbot/plugins/script.rb +48 -11
- data/data/rbot/plugins/search.rb +124 -28
- data/data/rbot/plugins/seen.rb +162 -31
- data/data/rbot/plugins/shortenurls.rb +1 -1
- data/data/rbot/plugins/slashdot.rb +19 -6
- data/data/rbot/plugins/spotify.rb +78 -0
- data/data/rbot/plugins/theyfightcrime.rb +10 -10
- data/data/rbot/plugins/time.rb +2 -2
- data/data/rbot/plugins/translator.rb +161 -85
- data/data/rbot/plugins/tube.rb +2 -2
- data/data/rbot/plugins/tumblr.rb +143 -0
- data/data/rbot/plugins/twitter.rb +25 -6
- data/data/rbot/plugins/urban.rb +6 -4
- data/data/rbot/plugins/url.rb +49 -10
- data/data/rbot/plugins/weather.rb +6 -6
- data/data/rbot/plugins/wserver.rb +5 -5
- data/data/rbot/plugins/youtube.rb +12 -12
- data/data/rbot/templates/lart/larts-italian +1 -1
- data/launch_here.rb +68 -0
- data/lib/rbot/botuser.rb +1 -1
- data/lib/rbot/compat19.rb +70 -0
- data/lib/rbot/config.rb +8 -6
- data/lib/rbot/core/auth.rb +37 -21
- data/lib/rbot/core/basics.rb +33 -2
- data/lib/rbot/core/config.rb +24 -17
- data/lib/rbot/core/filters_ui.rb +2 -2
- data/lib/rbot/core/irclog.rb +20 -11
- data/lib/rbot/core/remote.rb +9 -9
- data/lib/rbot/core/unicode.rb +4 -0
- data/lib/rbot/core/userdata.rb +16 -1
- data/lib/rbot/core/utils/extends.rb +76 -0
- data/lib/rbot/core/utils/filters.rb +47 -0
- data/lib/rbot/core/utils/httputil.rb +36 -26
- data/lib/rbot/core/utils/parse_time.rb +193 -0
- data/lib/rbot/core/utils/utils.rb +81 -56
- data/lib/rbot/core/utils/wordlist.rb +66 -0
- data/lib/rbot/core/wordlist_ui.rb +27 -0
- data/lib/rbot/irc.rb +59 -19
- data/lib/rbot/ircbot.rb +190 -58
- data/lib/rbot/ircsocket.rb +14 -8
- data/lib/rbot/language.rb +4 -3
- data/lib/rbot/load-gettext.rb +22 -9
- data/lib/rbot/message.rb +89 -18
- data/lib/rbot/messagemapper.rb +71 -19
- data/lib/rbot/plugins.rb +112 -44
- data/lib/rbot/{registry.rb → registry/bdb.rb} +226 -22
- data/lib/rbot/registry/tc.rb +531 -0
- data/lib/rbot/rfc2812.rb +33 -8
- data/lib/rbot/timer.rb +12 -20
- data/po/en_US/rbot-autorejoin.po +3 -0
- data/po/en_US/rbot-azgame.po +51 -43
- data/po/en_US/rbot-bash.po +15 -0
- data/po/en_US/rbot-dictclient.po +20 -20
- data/po/en_US/rbot-factoids.po +9 -9
- data/po/en_US/rbot-geoip.po +0 -0
- data/po/en_US/rbot-googlefight.po +24 -0
- data/po/en_US/rbot-grouphug.po +4 -4
- data/po/en_US/rbot-hangman.po +114 -0
- data/po/en_US/rbot-keywords.po +3 -3
- data/po/en_US/rbot-lastfm.po +268 -70
- data/po/en_US/rbot-markov.po +73 -2
- data/po/en_US/rbot-quotes.po +21 -21
- data/po/en_US/rbot-rss.po +6 -2
- data/po/en_US/rbot-script.po +3 -0
- data/po/en_US/rbot-seen.po +72 -0
- data/po/en_US/rbot-spell.po +2 -2
- data/po/en_US/rbot-translator.po +13 -13
- data/po/en_US/rbot-twitter.po +3 -3
- data/po/en_US/rbot-uno.po +131 -114
- data/po/en_US/rbot-wall.po +12 -13
- data/po/en_US/rbot-wheelfortune.po +41 -41
- data/po/en_US/rbot.po +254 -194
- data/po/fi/rbot-alias.po +82 -0
- data/po/fi/rbot-autoop.po +0 -0
- data/po/fi/rbot-autorejoin.po +20 -0
- data/po/fi/rbot-azgame.po +194 -0
- data/po/fi/rbot-bans.po +0 -0
- data/po/fi/rbot-bash.po +32 -0
- data/po/fi/rbot-botsnack.po +0 -0
- data/po/fi/rbot-cal.po +20 -0
- data/po/fi/rbot-chanserv.po +0 -0
- data/po/fi/rbot-chucknorris.po +0 -0
- data/po/fi/rbot-debugger.po +0 -0
- data/po/fi/rbot-deepthoughts.po +0 -0
- data/po/fi/rbot-delicious.po +0 -0
- data/po/fi/rbot-dice.po +0 -0
- data/po/fi/rbot-dict.po +0 -0
- data/po/fi/rbot-dictclient.po +111 -0
- data/po/fi/rbot-digg.po +0 -0
- data/po/fi/rbot-eightball.po +0 -0
- data/po/fi/rbot-excuse.po +0 -0
- data/po/fi/rbot-factoids.po +107 -0
- data/po/fi/rbot-figlet.po +36 -0
- data/po/fi/rbot-fish.po +0 -0
- data/po/fi/rbot-forecast.po +0 -0
- data/po/fi/rbot-fortune.po +0 -0
- data/po/fi/rbot-freshmeat.po +0 -0
- data/po/fi/rbot-geoip.po +0 -0
- data/po/fi/rbot-googlefight.po +24 -0
- data/po/fi/rbot-grouphug.po +35 -0
- data/po/fi/rbot-hangman.po +121 -0
- data/po/fi/rbot-hl2.po +0 -0
- data/po/fi/rbot-host.po +20 -0
- data/po/fi/rbot-imdb.po +0 -0
- data/po/fi/rbot-insult.po +0 -0
- data/po/fi/rbot-iplookup.po +0 -0
- data/po/fi/rbot-karma.po +0 -0
- data/po/fi/rbot-keywords.po +24 -0
- data/po/fi/rbot-lart.po +0 -0
- data/po/fi/rbot-lastfm.po +377 -0
- data/po/fi/rbot-linkbot.po +0 -0
- data/po/fi/rbot-markov.po +91 -0
- data/po/fi/rbot-math.po +0 -0
- data/po/fi/rbot-modes.po +0 -0
- data/po/fi/rbot-nickrecover.po +36 -0
- data/po/fi/rbot-nickserv.po +104 -0
- data/po/fi/rbot-nslookup.po +0 -0
- data/po/fi/rbot-quakeauth.po +0 -0
- data/po/fi/rbot-quiz.po +0 -0
- data/po/fi/rbot-quotes.po +108 -0
- data/po/fi/rbot-reaction.po +0 -0
- data/po/fi/rbot-remind.po +0 -0
- data/po/fi/rbot-remotectl.po +0 -0
- data/po/fi/rbot-ri.po +0 -0
- data/po/fi/rbot-roshambo.po +0 -0
- data/po/fi/rbot-rot13.po +0 -0
- data/po/fi/rbot-roulette.po +0 -0
- data/po/fi/rbot-rss.po +24 -0
- data/po/fi/rbot-salut.po +0 -0
- data/po/fi/rbot-script.po +20 -0
- data/po/fi/rbot-search.po +0 -0
- data/po/fi/rbot-seen.po +92 -0
- data/po/fi/rbot-shiritori.po +102 -0
- data/po/fi/rbot-shortenurls.po +0 -0
- data/po/fi/rbot-slashdot.po +0 -0
- data/po/fi/rbot-spell.po +54 -0
- data/po/fi/rbot-theyfightcrime.po +0 -0
- data/po/fi/rbot-threat.po +0 -0
- data/po/fi/rbot-time.po +0 -0
- data/po/fi/rbot-topic.po +0 -0
- data/po/fi/rbot-translator.po +77 -0
- data/po/fi/rbot-tube.po +0 -0
- data/po/fi/rbot-twitter.po +24 -0
- data/po/fi/rbot-uno.po +529 -0
- data/po/fi/rbot-urban.po +0 -0
- data/po/fi/rbot-url.po +0 -0
- data/po/fi/rbot-usermodes.po +0 -0
- data/po/fi/rbot-wall.po +32 -0
- data/po/fi/rbot-weather.po +0 -0
- data/po/fi/rbot-wheelfortune.po +205 -0
- data/po/fi/rbot-wow.po +0 -0
- data/po/fi/rbot-wserver.po +0 -0
- data/po/fi/rbot-youtube.po +58 -0
- data/po/fi/rbot.po +1152 -0
- data/po/fr/rbot-autorejoin.po +3 -0
- data/po/fr/rbot-azgame.po +51 -43
- data/po/fr/rbot-bash.po +15 -0
- data/po/fr/rbot-dictclient.po +20 -20
- data/po/fr/rbot-factoids.po +9 -9
- data/po/fr/rbot-geoip.po +0 -0
- data/po/fr/rbot-googlefight.po +24 -0
- data/po/fr/rbot-grouphug.po +4 -4
- data/po/fr/rbot-hangman.po +114 -0
- data/po/fr/rbot-keywords.po +3 -3
- data/po/fr/rbot-lastfm.po +268 -70
- data/po/fr/rbot-markov.po +74 -2
- data/po/fr/rbot-quotes.po +21 -21
- data/po/fr/rbot-rss.po +6 -2
- data/po/fr/rbot-script.po +3 -0
- data/po/fr/rbot-seen.po +72 -0
- data/po/fr/rbot-spell.po +2 -2
- data/po/fr/rbot-translator.po +13 -13
- data/po/fr/rbot-twitter.po +3 -3
- data/po/fr/rbot-uno.po +132 -114
- data/po/fr/rbot-wall.po +8 -9
- data/po/fr/rbot-wheelfortune.po +41 -41
- data/po/fr/rbot.po +268 -197
- data/po/it/rbot-autorejoin.po +3 -0
- data/po/it/rbot-azgame.po +50 -42
- data/po/it/rbot-bash.po +15 -0
- data/po/it/rbot-dictclient.po +20 -20
- data/po/it/rbot-factoids.po +9 -9
- data/po/it/rbot-geoip.po +0 -0
- data/po/it/rbot-googlefight.po +24 -0
- data/po/it/rbot-grouphug.po +4 -4
- data/po/it/rbot-hangman.po +114 -0
- data/po/it/rbot-keywords.po +3 -3
- data/po/it/rbot-lastfm.po +268 -70
- data/po/it/rbot-markov.po +75 -3
- data/po/it/rbot-quotes.po +21 -21
- data/po/it/rbot-rss.po +7 -3
- data/po/it/rbot-script.po +19 -0
- data/po/it/rbot-seen.po +72 -0
- data/po/it/rbot-spell.po +2 -2
- data/po/it/rbot-translator.po +13 -13
- data/po/it/rbot-twitter.po +3 -3
- data/po/it/rbot-uno.po +137 -116
- data/po/it/rbot-wall.po +8 -9
- data/po/it/rbot-wheelfortune.po +41 -41
- data/po/it/rbot.po +265 -208
- data/po/ja/rbot-autorejoin.po +3 -0
- data/po/ja/rbot-azgame.po +51 -43
- data/po/ja/rbot-bash.po +15 -0
- data/po/ja/rbot-dictclient.po +20 -20
- data/po/ja/rbot-factoids.po +9 -9
- data/po/ja/rbot-geoip.po +0 -0
- data/po/ja/rbot-googlefight.po +24 -0
- data/po/ja/rbot-grouphug.po +4 -4
- data/po/ja/rbot-hangman.po +114 -0
- data/po/ja/rbot-keywords.po +3 -3
- data/po/ja/rbot-lastfm.po +268 -70
- data/po/ja/rbot-markov.po +73 -2
- data/po/ja/rbot-quotes.po +21 -21
- data/po/ja/rbot-rss.po +6 -2
- data/po/ja/rbot-script.po +3 -0
- data/po/ja/rbot-seen.po +72 -0
- data/po/ja/rbot-spell.po +2 -2
- data/po/ja/rbot-translator.po +13 -13
- data/po/ja/rbot-twitter.po +3 -3
- data/po/ja/rbot-uno.po +131 -114
- data/po/ja/rbot-wall.po +8 -9
- data/po/ja/rbot-wheelfortune.po +41 -41
- data/po/ja/rbot.po +248 -192
- data/po/rbot-alias.pot +2 -2
- data/po/rbot-autorejoin.pot +21 -0
- data/po/rbot-azgame.pot +51 -43
- data/po/rbot-bash.pot +33 -0
- data/po/rbot-cal.pot +2 -2
- data/po/rbot-dictclient.pot +21 -21
- data/po/rbot-factoids.pot +10 -10
- data/po/rbot-figlet.pot +2 -2
- data/po/rbot-geoip.pot +0 -0
- data/po/rbot-googlefight.pot +25 -0
- data/po/rbot-grouphug.pot +6 -6
- data/po/rbot-hangman.pot +115 -0
- data/po/rbot-host.pot +2 -2
- data/po/rbot-keywords.pot +4 -4
- data/po/rbot-lastfm.pot +270 -72
- data/po/rbot-markov.pot +74 -3
- data/po/rbot-nickrecover.pot +2 -2
- data/po/rbot-nickserv.pot +2 -2
- data/po/rbot-quotes.pot +22 -22
- data/po/rbot-rss.pot +7 -3
- data/po/rbot-script.pot +21 -0
- data/po/rbot-seen.pot +90 -0
- data/po/rbot-shiritori.pot +2 -2
- data/po/rbot-spell.pot +3 -3
- data/po/rbot-translator.pot +14 -14
- data/po/rbot-twitter.pot +4 -4
- data/po/rbot-uno.pot +132 -115
- data/po/rbot-wall.pot +2 -2
- data/po/rbot-wheelfortune.pot +42 -42
- data/po/rbot-youtube.pot +2 -2
- data/po/rbot.pot +249 -193
- data/po/zh_CN/rbot-autorejoin.po +3 -0
- data/po/zh_CN/rbot-azgame.po +50 -42
- data/po/zh_CN/rbot-bash.po +15 -0
- data/po/zh_CN/rbot-dictclient.po +20 -20
- data/po/zh_CN/rbot-factoids.po +9 -9
- data/po/zh_CN/rbot-geoip.po +0 -0
- data/po/zh_CN/rbot-googlefight.po +24 -0
- data/po/zh_CN/rbot-grouphug.po +4 -4
- data/po/zh_CN/rbot-hangman.po +114 -0
- data/po/zh_CN/rbot-keywords.po +3 -3
- data/po/zh_CN/rbot-lastfm.po +268 -70
- data/po/zh_CN/rbot-markov.po +73 -2
- data/po/zh_CN/rbot-quotes.po +21 -21
- data/po/zh_CN/rbot-rss.po +6 -2
- data/po/zh_CN/rbot-script.po +3 -0
- data/po/zh_CN/rbot-seen.po +72 -0
- data/po/zh_CN/rbot-spell.po +2 -2
- data/po/zh_CN/rbot-translator.po +13 -13
- data/po/zh_CN/rbot-twitter.po +3 -3
- data/po/zh_CN/rbot-uno.po +131 -114
- data/po/zh_CN/rbot-wall.po +7 -8
- data/po/zh_CN/rbot-wheelfortune.po +41 -41
- data/po/zh_CN/rbot.po +248 -192
- data/po/zh_TW/rbot-autorejoin.po +3 -0
- data/po/zh_TW/rbot-azgame.po +50 -42
- data/po/zh_TW/rbot-bash.po +15 -0
- data/po/zh_TW/rbot-dictclient.po +20 -20
- data/po/zh_TW/rbot-factoids.po +9 -9
- data/po/zh_TW/rbot-geoip.po +0 -0
- data/po/zh_TW/rbot-googlefight.po +24 -0
- data/po/zh_TW/rbot-grouphug.po +4 -4
- data/po/zh_TW/rbot-hangman.po +114 -0
- data/po/zh_TW/rbot-keywords.po +3 -3
- data/po/zh_TW/rbot-lastfm.po +268 -70
- data/po/zh_TW/rbot-markov.po +73 -2
- data/po/zh_TW/rbot-quotes.po +21 -21
- data/po/zh_TW/rbot-rss.po +6 -2
- data/po/zh_TW/rbot-script.po +3 -0
- data/po/zh_TW/rbot-seen.po +72 -0
- data/po/zh_TW/rbot-spell.po +2 -2
- data/po/zh_TW/rbot-translator.po +13 -13
- data/po/zh_TW/rbot-twitter.po +3 -3
- data/po/zh_TW/rbot-uno.po +131 -114
- data/po/zh_TW/rbot-wall.po +7 -8
- data/po/zh_TW/rbot-wheelfortune.po +41 -41
- data/po/zh_TW/rbot.po +253 -194
- data/setup.rb +4 -4
- metadata +127 -18
- data/README +0 -43
- data/data/rbot/plugins/fish.rb +0 -121
- data/lib/rbot/dbhash.rb +0 -199
@@ -116,9 +116,10 @@ class Keywords < Plugin
|
|
116
116
|
scan
|
117
117
|
|
118
118
|
# import old format keywords into DBHash
|
119
|
-
|
119
|
+
olds = @bot.path 'keywords.rbot'
|
120
|
+
if File.exist? olds
|
120
121
|
log "auto importing old keywords.rbot"
|
121
|
-
IO.foreach(
|
122
|
+
IO.foreach(olds) do |line|
|
122
123
|
if(line =~ /^(.*?)\s*<=(is|are)?=?>\s*(.*)$/)
|
123
124
|
lhs = $1
|
124
125
|
mhs = $2
|
@@ -129,7 +130,7 @@ class Keywords < Plugin
|
|
129
130
|
@keywords[lhs] = Keyword.new(mhs, values).dump
|
130
131
|
end
|
131
132
|
end
|
132
|
-
File.rename(
|
133
|
+
File.rename(olds, olds + ".old")
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
@@ -137,15 +138,12 @@ class Keywords < Plugin
|
|
137
138
|
# have been added
|
138
139
|
def scan
|
139
140
|
# first scan for old DBHash files, and convert them
|
140
|
-
Dir[
|
141
|
+
Dir[datafile('*')].each {|f|
|
141
142
|
next unless f =~ /\.db$/
|
142
143
|
log "upgrading keyword db #{f} (rbot 0.9.5 or prior) database format"
|
143
144
|
newname = f.gsub(/\.db$/, ".kdb")
|
144
|
-
old = BDB::Hash.open f, nil,
|
145
|
-
|
146
|
-
new = BDB::CIBtree.open(newname, nil,
|
147
|
-
BDB::CREATE | BDB::EXCL,
|
148
|
-
0600)
|
145
|
+
old = BDB::Hash.open f, nil, "r+", 0600
|
146
|
+
new = BDB::CIBtree.open(newname, nil, BDB::CREATE | BDB::EXCL, 0600)
|
149
147
|
old.each {|k,v|
|
150
148
|
new[k] = v
|
151
149
|
}
|
@@ -155,7 +153,7 @@ class Keywords < Plugin
|
|
155
153
|
}
|
156
154
|
|
157
155
|
# then scan for current DBTree files, and load them
|
158
|
-
Dir[
|
156
|
+
Dir[@bot.path('keywords', '*')].each {|f|
|
159
157
|
next unless f =~ /\.kdb$/
|
160
158
|
hsh = DBTree.new @bot, f, true
|
161
159
|
key = File.basename(f).gsub(/\.kdb$/, "")
|
@@ -164,7 +162,7 @@ class Keywords < Plugin
|
|
164
162
|
}
|
165
163
|
|
166
164
|
# then scan for non DB files, and convert/import them and delete
|
167
|
-
Dir[
|
165
|
+
Dir[@bot.path('keywords', '*')].each {|f|
|
168
166
|
next if f =~ /\.kdb$/
|
169
167
|
next if f =~ /CVS$/
|
170
168
|
log "auto converting keywords from #{f}"
|
@@ -192,28 +190,28 @@ class Keywords < Plugin
|
|
192
190
|
|
193
191
|
# upgrade data files found in old rbot formats to current
|
194
192
|
def upgrade_data
|
195
|
-
|
193
|
+
olds = @bot.path 'keywords.db'
|
194
|
+
if File.exist? olds
|
196
195
|
log "upgrading old keywords (rbot 0.9.5 or prior) database format"
|
197
|
-
old = BDB::Hash.open "
|
198
|
-
"r+", 0600
|
196
|
+
old = BDB::Hash.open olds, nil, "r+", 0600
|
199
197
|
old.each {|k,v|
|
200
198
|
@keywords[k] = v
|
201
199
|
}
|
202
200
|
old.close
|
203
201
|
@keywords.flush
|
204
|
-
File.rename(
|
202
|
+
File.rename(olds, olds + ".old")
|
205
203
|
end
|
206
204
|
|
207
|
-
|
205
|
+
olds.replace(@bot.path('keyword.db'))
|
206
|
+
if File.exist? olds
|
208
207
|
log "upgrading old keywords (rbot 0.9.9 or prior) database format"
|
209
|
-
old = BDB::CIBtree.open "
|
210
|
-
"r+", 0600
|
208
|
+
old = BDB::CIBtree.open olds, nil, "r+", 0600
|
211
209
|
old.each {|k,v|
|
212
210
|
@keywords[k] = v
|
213
211
|
}
|
214
212
|
old.close
|
215
213
|
@keywords.flush
|
216
|
-
File.rename(
|
214
|
+
File.rename(olds, olds + ".old")
|
217
215
|
end
|
218
216
|
end
|
219
217
|
|
@@ -223,7 +221,7 @@ class Keywords < Plugin
|
|
223
221
|
end
|
224
222
|
|
225
223
|
def oldsave
|
226
|
-
File.open(
|
224
|
+
File.open(@bot.path("keywords.rbot"), "w") do |file|
|
227
225
|
@keywords.each do |key, value|
|
228
226
|
file.puts "#{key}<=#{value.type}=>#{value.dump}"
|
229
227
|
end
|
@@ -327,6 +325,12 @@ class Keywords < Plugin
|
|
327
325
|
case plugin
|
328
326
|
when /keyword/
|
329
327
|
case topic
|
328
|
+
when 'export'
|
329
|
+
'keyword export => exports definitions to keyword_factoids.rbot'
|
330
|
+
when 'stats'
|
331
|
+
'keyword stats => show statistics about static facts'
|
332
|
+
when 'wipe'
|
333
|
+
'keyword wipe <keyword> => forgets everything about a keyword'
|
330
334
|
when 'lookup'
|
331
335
|
'keyword [lookup] <keyword> => look up the definition for a keyword; writing "lookup" is optional'
|
332
336
|
when 'set'
|
@@ -350,7 +354,7 @@ class Keywords < Plugin
|
|
350
354
|
when '<topic>'
|
351
355
|
'<topic> => respond by setting the topic to the rest of the definition'
|
352
356
|
else
|
353
|
-
'keyword module (fact learning and regurgitation) topics: lookup, set, forget, tell, search, listen, address, <reply>, <action>, <who>, <topic>'
|
357
|
+
'keyword module (fact learning and regurgitation) topics: lookup, set, forget, tell, search, listen, address, stats, export, wipe, <reply>, <action>, <who>, <topic>'
|
354
358
|
end
|
355
359
|
when "forget"
|
356
360
|
'forget <keyword> => forget a keyword'
|
@@ -484,7 +488,7 @@ class Keywords < Plugin
|
|
484
488
|
|
485
489
|
# TODO check factoids config
|
486
490
|
# also TODO: runtime export
|
487
|
-
dir =
|
491
|
+
dir = @bot.path 'factoids'
|
488
492
|
fname = File.join(dir,"keyword_factoids.rbot")
|
489
493
|
|
490
494
|
Dir.mkdir(dir) unless FileTest.directory?(dir)
|
data/data/rbot/plugins/lart.rb
CHANGED
@@ -41,12 +41,12 @@ class LartPlugin < Plugin
|
|
41
41
|
|
42
42
|
# We may be on an old installation, so on the first run read non-language-specific larts
|
43
43
|
unless defined?(@oldlart)
|
44
|
-
@oldlart =
|
45
|
-
@oldpraise =
|
44
|
+
@oldlart = datafile 'larts'
|
45
|
+
@oldpraise = datafile 'praise'
|
46
46
|
end
|
47
47
|
|
48
|
-
@lartfile.replace
|
49
|
-
@praisefile.replace
|
48
|
+
@lartfile.replace(datafile("larts-#{lang}"))
|
49
|
+
@praisefile.replace(datafile("praises-#{lang}"))
|
50
50
|
@larts.clear
|
51
51
|
@praises.clear
|
52
52
|
if File.exists? @lartfile
|
@@ -72,8 +72,7 @@ class LartPlugin < Plugin
|
|
72
72
|
|
73
73
|
def save
|
74
74
|
return unless @changed
|
75
|
-
Dir.mkdir(
|
76
|
-
# TODO implement safe saving here too
|
75
|
+
Dir.mkdir(datafile) unless FileTest.directory? datafile
|
77
76
|
Utils.safe_save(@lartfile) { |file|
|
78
77
|
file.puts @larts
|
79
78
|
}
|
data/data/rbot/plugins/lastfm.rb
CHANGED
@@ -6,10 +6,12 @@
|
|
6
6
|
# Author:: Jeremy Voorhis
|
7
7
|
# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
|
8
8
|
# Author:: Casey Link <unnamedrambler@gmail.com>
|
9
|
+
# Author:: Raine Virta <rane@kapsi.fi>
|
9
10
|
#
|
10
11
|
# Copyright:: (C) 2005 Jeremy Voorhis
|
11
12
|
# Copyright:: (C) 2007 Giuseppe Bilotta
|
12
13
|
# Copyright:: (C) 2008 Casey Link
|
14
|
+
# Copyright:: (C) 2009 Raine Virta
|
13
15
|
#
|
14
16
|
# License:: GPL v2
|
15
17
|
|
@@ -17,6 +19,8 @@ require 'rexml/document'
|
|
17
19
|
require 'cgi'
|
18
20
|
|
19
21
|
class ::LastFmEvent
|
22
|
+
attr_reader :attendance, :date
|
23
|
+
|
20
24
|
def initialize(hash)
|
21
25
|
@url = hash[:url]
|
22
26
|
@date = hash[:date]
|
@@ -36,15 +40,26 @@ class ::LastFmEvent
|
|
36
40
|
end
|
37
41
|
|
38
42
|
def compact_display
|
39
|
-
|
40
|
-
return "%s %s @ %s (%s attending) %s" % [@date.strftime("%a %b
|
43
|
+
unless @attendance.zero?
|
44
|
+
return "%s %s @ %s (%s attending) %s" % [@date.strftime("%a, %b %d"), @artist_string, @location, @attendance, @url]
|
41
45
|
end
|
42
|
-
return "%s %s @ %s %s" % [@date.strftime("%a %b
|
46
|
+
return "%s %s @ %s %s" % [@date.strftime("%a, %b %d"), @artist_string, @location, @url]
|
43
47
|
end
|
44
48
|
alias :to_s :compact_display
|
45
49
|
|
46
50
|
end
|
47
51
|
|
52
|
+
define_structure :LastFmVenue, :id, :city, :street, :postal, :country, :name, :url, :lat, :long
|
53
|
+
class ::Struct::LastFmVenue
|
54
|
+
def to_s
|
55
|
+
str = self.name.dup
|
56
|
+
if self.country
|
57
|
+
str << " (" << [self.city, self.country].compact.join(", ") << ")"
|
58
|
+
end
|
59
|
+
str
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
48
63
|
class LastFmPlugin < Plugin
|
49
64
|
include REXML
|
50
65
|
Config.register Config::IntegerValue.new('lastfm.max_events',
|
@@ -53,6 +68,18 @@ class LastFmPlugin < Plugin
|
|
53
68
|
Config.register Config::IntegerValue.new('lastfm.default_events',
|
54
69
|
:default => 3, :validate => Proc.new{|v| v > 1},
|
55
70
|
:desc => "Default number of events to display.")
|
71
|
+
Config.register Config::IntegerValue.new('lastfm.max_shouts',
|
72
|
+
:default => 5, :validate => Proc.new{|v| v > 1},
|
73
|
+
:desc => "Maximum number of user shouts to display.")
|
74
|
+
Config.register Config::IntegerValue.new('lastfm.default_shouts',
|
75
|
+
:default => 3, :validate => Proc.new{|v| v > 1},
|
76
|
+
:desc => "Default number of user shouts to display.")
|
77
|
+
Config.register Config::IntegerValue.new('lastfm.max_user_data',
|
78
|
+
:default => 25, :validate => Proc.new{|v| v > 1},
|
79
|
+
:desc => "Maximum number of user data entries (except events and shouts) to display.")
|
80
|
+
Config.register Config::IntegerValue.new('lastfm.default_user_data',
|
81
|
+
:default => 10, :validate => Proc.new{|v| v > 1},
|
82
|
+
:desc => "Default number of user data entries (except events and shouts) to display.")
|
56
83
|
|
57
84
|
APIKEY = "b25b959554ed76058ac220b7b2e0a026"
|
58
85
|
APIURL = "http://ws.audioscrobbler.com/2.0/?api_key=#{APIKEY}&"
|
@@ -70,9 +97,10 @@ class LastFmPlugin < Plugin
|
|
70
97
|
end
|
71
98
|
|
72
99
|
def help(plugin, topic="")
|
100
|
+
period = _(", where <period> can be one of: 3|6|12 months, a year")
|
73
101
|
case (topic.intern rescue nil)
|
74
102
|
when :event, :events
|
75
|
-
_("lastfm [<num>] events in <location> => show information on events in or near <location>. lastfm [<num>] events by <artist/group> => show information on events by <artist/group>. The number of events <num> that can be displayed is optional, defaults to %{d} and cannot be higher than %{m}") % {:d => @bot.config['lastfm.default_events'], :m => @bot.config['lastfm.max_events']}
|
103
|
+
_("lastfm [<num>] events in <location> => show information on events in or near <location>. lastfm [<num>] events by <artist/group> => show information on events by <artist/group>. lastfm [<num>] events at <venue> => show information on events at specific <venue>. The number of events <num> that can be displayed is optional, defaults to %{d} and cannot be higher than %{m}. Append 'sort by <what> [in <order> order]' to sort events. Events can be sorted by attendance or date (default) in ascending or descending order.") % {:d => @bot.config['lastfm.default_events'], :m => @bot.config['lastfm.max_events']}
|
76
104
|
when :artist
|
77
105
|
_("lastfm artist <name> => show information on artist <name> from last.fm")
|
78
106
|
when :album
|
@@ -80,31 +108,113 @@ class LastFmPlugin < Plugin
|
|
80
108
|
when :track
|
81
109
|
_("lastfm track <name> => search tracks matching <name> on last.fm")
|
82
110
|
when :now, :np
|
83
|
-
_("lastfm now [<
|
111
|
+
_("lastfm now [<nick>] => show the now playing track from last.fm. np [<nick>] does the same.")
|
84
112
|
when :set
|
85
|
-
_("lastfm set
|
113
|
+
_("lastfm set user <user> => associate your current irc nick with a last.fm user. lastfm set verb <present>, <past> => set your preferred now playing/just played verbs. default \"is listening to\" and \"listened to\".")
|
86
114
|
when :who
|
87
|
-
_("lastfm who [<nick>] => show who <nick> is
|
115
|
+
_("lastfm who [<nick>] => show who <nick> is on last.fm. if <nick> is empty, show who you are on lastfm.")
|
88
116
|
when :compare
|
89
|
-
_("lastfm compare <nick1> <nick2> => show musical taste compatibility between nick1 and nick2
|
117
|
+
_("lastfm compare [<nick1>] <nick2> => show musical taste compatibility between nick1 (or user if omitted) and nick2")
|
118
|
+
when :shouts
|
119
|
+
_("lastfm shouts [<nick>] => show shouts to <nick>")
|
120
|
+
when :friends
|
121
|
+
_("lastfm friends [<nick>] => show <nick>'s friends")
|
122
|
+
when :neighbors, :neighbours
|
123
|
+
_("lastfm neighbors [<nick>] => show people who share similar musical taste as <nick>")
|
124
|
+
when :lovedtracks
|
125
|
+
_("lastfm loved[tracks] [<nick>] => show tracks that <nick> has loved")
|
126
|
+
when :recenttracks, :recentracks
|
127
|
+
_("lastfm recent[tracks] [<nick>] => show tracks that <nick> has recently played")
|
128
|
+
when :topalbums
|
129
|
+
_("lastfm topalbums [<nick>] [over <period>] => show <nick>'s top albums%{p}") % { :p => period }
|
130
|
+
when :topartists
|
131
|
+
_("lastfm topartists [<nick>] [over <period>] => show <nick>'s top artists%{p}") % { :p => period }
|
132
|
+
when :toptracks
|
133
|
+
_("lastfm toptracks [<nick>] [over <period>] => show <nick>'s top tracks%{p}") % { :p => period }
|
134
|
+
when :weeklyalbumchart
|
135
|
+
_("lastfm weeklyalbumchart [<nick>] => show <nick>'s weekly album chart")
|
136
|
+
when :weeklyartistchart
|
137
|
+
_("lastfm weeklyartistchart [<nick>] => show <nick>'s weekly artist chart")
|
138
|
+
when :weeklytrackchart
|
139
|
+
_("lastfm weeklyartistchart [<nick>] => show <nick>'s weekly track chart")
|
90
140
|
else
|
91
|
-
_("
|
141
|
+
_("last.fm plugin - topics: events, artist, album, track, now, set, who, compare, shouts, friends, neighbors, (loved|recent)tracks, top(albums|tracks|artists), weekly(album|artist|track)chart")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO allow searching by country etc.
|
146
|
+
#
|
147
|
+
# Options: name, limit
|
148
|
+
def search_venue_by(options)
|
149
|
+
params = {}
|
150
|
+
params[:venue] = CGI.escape(options[:name])
|
151
|
+
options.delete(:name)
|
152
|
+
params.merge!(options)
|
153
|
+
|
154
|
+
uri = "#{APIURL}method=venue.search&"
|
155
|
+
uri << params.to_a.map {|e| e.join("=")}.join("&")
|
156
|
+
|
157
|
+
xml = @bot.httputil.get_response(uri)
|
158
|
+
doc = Document.new xml.body
|
159
|
+
results = []
|
160
|
+
|
161
|
+
doc.root.elements.each("results/venuematches/venue") do |v|
|
162
|
+
venue = LastFmVenue.new
|
163
|
+
venue.id = v.elements["id"].text.to_i
|
164
|
+
venue.url = v.elements["url"].text
|
165
|
+
venue.lat = v.elements["location/geo:point/geo:lat"].text.to_f
|
166
|
+
venue.long = v.elements["location/geo:point/geo:long"].text.to_f
|
167
|
+
venue.name = v.elements["name"].text
|
168
|
+
venue.city = v.elements["location/city"].text
|
169
|
+
venue.street = v.elements["location/street"].text
|
170
|
+
venue.postal = v.elements["location/postalcode"].text
|
171
|
+
venue.country = v.elements["location/country"].text
|
172
|
+
|
173
|
+
results << venue
|
92
174
|
end
|
175
|
+
results
|
93
176
|
end
|
94
177
|
|
95
178
|
def find_events(m, params)
|
96
179
|
num = params[:num] || @bot.config['lastfm.default_events']
|
97
180
|
num = num.to_i.clip(1, @bot.config['lastfm.max_events'])
|
98
181
|
|
99
|
-
|
100
|
-
|
101
|
-
|
182
|
+
sort_by = params[:sort_by] || :date
|
183
|
+
sort_order = params[:sort_order]
|
184
|
+
sort_order = sort_order.to_sym unless sort_order.nil?
|
102
185
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
186
|
+
location = params[:location]
|
187
|
+
artist = params[:who]
|
188
|
+
venue = params[:venue]
|
189
|
+
user = resolve_username(m, params[:user])
|
190
|
+
|
191
|
+
if location
|
192
|
+
uri = "#{APIURL}method=geo.getevents&location=#{CGI.escape location.to_s}"
|
193
|
+
emptymsg = _("no events found in %{location}") % {:location => location.to_s}
|
194
|
+
elsif venue
|
195
|
+
begin
|
196
|
+
venues = search_venue_by(:name => venue.to_s, :limit => 1)
|
197
|
+
rescue Exception => e
|
198
|
+
error e
|
199
|
+
m.reply _("an error occurred looking for venue %{venue}: %{e}") % {
|
200
|
+
:venue => venue.to_s,
|
201
|
+
:e => e.message
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
if venues.empty?
|
206
|
+
m.reply _("no venue found matching %{venue}") % {:venue => venue.to_s}
|
207
|
+
return
|
208
|
+
end
|
209
|
+
venue = venues.first
|
210
|
+
uri = "#{APIURL}method=venue.getevents&venue=#{venue.id}"
|
211
|
+
emptymsg = _("no events found at %{venue}") % {:venue => venue.to_s}
|
212
|
+
elsif artist
|
213
|
+
uri = "#{APIURL}method=artist.getevents&artist=#{CGI.escape artist.to_s}"
|
214
|
+
emptymsg = _("no events found by %{artist}") % {:artist => artist.to_s}
|
215
|
+
elsif user
|
216
|
+
uri = "#{APIURL}method=user.getevents&user=#{CGI.escape user}"
|
217
|
+
emptymsg = _("%{user} is not attending any events") % {:user => user}
|
108
218
|
end
|
109
219
|
xml = @bot.httputil.get_response(uri)
|
110
220
|
|
@@ -113,7 +223,7 @@ class LastFmPlugin < Plugin
|
|
113
223
|
if doc.root and doc.root.attributes["status"] == "failed"
|
114
224
|
m.reply doc.root.elements["error"].text
|
115
225
|
else
|
116
|
-
m.reply _("
|
226
|
+
m.reply _("could not retrieve events")
|
117
227
|
end
|
118
228
|
end
|
119
229
|
disp_events = Array.new
|
@@ -129,11 +239,7 @@ class LastFmPlugin < Plugin
|
|
129
239
|
h[:date] = Time.utc(date[3].to_i, date[2], date[1].to_i)
|
130
240
|
h[:desc] = e.elements["description"].text
|
131
241
|
h[:url] = e.elements["url"].text
|
132
|
-
e.
|
133
|
-
if node.kind_of? Element and node.attributes["name"] == "attendance" then
|
134
|
-
h[:attendance] = node.text
|
135
|
-
end
|
136
|
-
}
|
242
|
+
h[:attendance] = e.elements["attendance"].text.to_i
|
137
243
|
artists = Array.new
|
138
244
|
e.elements.each("artists/artist"){ |a|
|
139
245
|
artists << a.text
|
@@ -141,47 +247,54 @@ class LastFmPlugin < Plugin
|
|
141
247
|
h[:artists] = artists
|
142
248
|
events << LastFmEvent.new(h)
|
143
249
|
}
|
250
|
+
if events.empty?
|
251
|
+
m.reply emptymsg
|
252
|
+
return
|
253
|
+
end
|
254
|
+
|
255
|
+
# sort order when sorted by date is ascending by default
|
256
|
+
# and descending when sorted by attendance
|
257
|
+
case sort_by.to_sym
|
258
|
+
when :attendance
|
259
|
+
events = events.sort_by { |e| e.attendance }.reverse
|
260
|
+
events.reverse! if [:ascending, :asc].include? sort_order
|
261
|
+
when :date
|
262
|
+
events = events.sort_by { |e| e.date }
|
263
|
+
events.reverse! if [:descending, :desc].include? sort_order
|
264
|
+
end
|
265
|
+
|
144
266
|
events[0...num].each { |event|
|
145
267
|
disp_events << event.to_s
|
146
268
|
}
|
147
269
|
m.reply disp_events.join(' | '), :split_at => /\s+\|\s+/
|
148
270
|
|
149
|
-
end
|
271
|
+
end
|
150
272
|
|
151
273
|
def tasteometer(m, params)
|
152
274
|
opts = { :cache => false }
|
153
|
-
user1 = params[:user1]
|
154
|
-
user2 = params[:user2]
|
275
|
+
user1 = resolve_username(m, params[:user1])
|
276
|
+
user2 = resolve_username(m, params[:user2])
|
155
277
|
xml = @bot.httputil.get_response("#{APIURL}method=tasteometer.compare&type1=user&type2=user&value1=#{CGI.escape user1}&value2=#{CGI.escape user2}", opts)
|
156
278
|
doc = Document.new xml.body
|
157
279
|
unless doc
|
158
280
|
m.reply _("last.fm parsing failed")
|
159
281
|
return
|
160
282
|
end
|
161
|
-
if xml.class == Net::
|
162
|
-
if doc.root.elements["error"].attributes["code"] == "7" then
|
283
|
+
if xml.class == Net::HTTPBadRequest
|
284
|
+
if doc.root.elements["error"].attributes["code"] == "7" then
|
163
285
|
error = doc.root.elements["error"].text
|
164
286
|
error.match(/Invalid username: \[(.*)\]/);
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
params[:user2] = @registry[ $1 ]
|
170
|
-
end
|
171
|
-
params[:recurs] = true
|
172
|
-
tasteometer(m, params)
|
173
|
-
else
|
174
|
-
m.reply _("%{u} doesn't exist at last.fm. Perhaps you need to: lastfm set <username>") % {:u => baduser}
|
175
|
-
return
|
176
|
-
end
|
287
|
+
baduser = $1
|
288
|
+
|
289
|
+
m.reply _("%{u} doesn't exist on last.fm") % {:u => baduser}
|
290
|
+
return
|
177
291
|
else
|
178
|
-
m.reply _("
|
292
|
+
m.reply _("error: %{e}") % {:e => doc.root.element["error"].text}
|
179
293
|
return
|
180
294
|
end
|
181
295
|
end
|
182
|
-
now = artist = track = albumtxt = date = nil
|
183
296
|
score = doc.root.elements["comparison/result/score"].text.to_f
|
184
|
-
|
297
|
+
artists = doc.root.get_elements("comparison/result/artists/artist").map { |e| e.elements["name"].text}
|
185
298
|
case
|
186
299
|
when score >= 0.9
|
187
300
|
rating = _("Super")
|
@@ -196,19 +309,26 @@ class LastFmPlugin < Plugin
|
|
196
309
|
else
|
197
310
|
rating = _("Very Low")
|
198
311
|
end
|
199
|
-
|
312
|
+
|
313
|
+
common_artists = unless artists.empty?
|
314
|
+
_(" and music they have in common includes: %{artists}") % {
|
315
|
+
:artists => Utils.comma_list(artists) }
|
316
|
+
else
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
m.reply _("%{a}'s and %{b}'s musical compatibility rating is %{bold}%{r}%{bold}%{common}") % {
|
321
|
+
:a => user1,
|
322
|
+
:b => user2,
|
323
|
+
:r => rating.downcase,
|
324
|
+
:bold => Bold,
|
325
|
+
:common => common_artists
|
326
|
+
}
|
200
327
|
end
|
201
328
|
|
202
329
|
def now_playing(m, params)
|
203
330
|
opts = { :cache => false }
|
204
|
-
user =
|
205
|
-
if params[:who]
|
206
|
-
user = params[:who].to_s
|
207
|
-
elsif @registry.has_key? m.sourcenick
|
208
|
-
user = @registry[ m.sourcenick ]
|
209
|
-
else
|
210
|
-
user = m.sourcenick
|
211
|
-
end
|
331
|
+
user = resolve_username(m, params[:who])
|
212
332
|
xml = @bot.httputil.get_response("#{APIURL}method=user.getrecenttracks&user=#{CGI.escape user}", opts)
|
213
333
|
doc = Document.new xml.body
|
214
334
|
unless doc
|
@@ -216,17 +336,13 @@ class LastFmPlugin < Plugin
|
|
216
336
|
return
|
217
337
|
end
|
218
338
|
if xml.class == Net::HTTPBadRequest
|
219
|
-
if doc.root.elements["error"].
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
else
|
225
|
-
m.reply "#{user} doesn't exist at last.fm. Perhaps you need to: lastfm set <username>"
|
226
|
-
return
|
227
|
-
end
|
339
|
+
if doc.root.elements["error"].attributes["code"] == "6" then
|
340
|
+
m.reply _("%{user} doesn't exist on last.fm, perhaps they need to: lastfm user <username>") % {
|
341
|
+
:user => user
|
342
|
+
}
|
343
|
+
return
|
228
344
|
else
|
229
|
-
m.reply _("
|
345
|
+
m.reply _("error: %{e}") % {:e => doc.root.element["error"].text}
|
230
346
|
return
|
231
347
|
end
|
232
348
|
end
|
@@ -240,46 +356,81 @@ class LastFmPlugin < Plugin
|
|
240
356
|
artist = first.elements["artist"].text
|
241
357
|
track = first.elements["name"].text
|
242
358
|
albumtxt = first.elements["album"].text
|
243
|
-
album =
|
244
|
-
if albumtxt
|
359
|
+
album = if albumtxt
|
245
360
|
year = get_album(artist, albumtxt)[2]
|
246
|
-
|
361
|
+
if year
|
362
|
+
_(" [%{albumtext}, %{year}]") % { :albumtext => albumtxt, :year => year }
|
363
|
+
else
|
364
|
+
_(" [%{albumtext}]") % { :albumtext => albumtxt }
|
365
|
+
end
|
366
|
+
else
|
367
|
+
nil
|
368
|
+
end
|
369
|
+
past = nil
|
370
|
+
date = XPath.first(first, "//date")
|
371
|
+
if date != nil
|
372
|
+
time = date.attributes["uts"]
|
373
|
+
past = Time.at(time.to_i)
|
247
374
|
end
|
248
|
-
date = first.elements["date"].attributes["uts"]
|
249
|
-
past = Time.at(date.to_i)
|
250
375
|
if now == "true"
|
251
|
-
verb = _("listening")
|
376
|
+
verb = _("is listening to")
|
252
377
|
if @registry.has_key? "#{m.sourcenick}_verb_present"
|
253
378
|
verb = @registry["#{m.sourcenick}_verb_present"]
|
254
379
|
end
|
255
|
-
|
380
|
+
reply = _("%{u} %{v} \"%{t}\" by %{bold}%{a}%{bold}%{b}") % {:u => user, :v => verb, :t => track, :a => artist, :b => album, :bold => Bold}
|
256
381
|
else
|
257
|
-
verb = _("listened")
|
382
|
+
verb = _("listened to")
|
258
383
|
if @registry.has_key? "#{m.sourcenick}_verb_past"
|
259
384
|
verb = @registry["#{m.sourcenick}_verb_past"]
|
260
385
|
end
|
261
386
|
ago = Utils.timeago(past)
|
262
|
-
|
387
|
+
reply = _("%{u} %{v} \"%{t}\" by %{bold}%{a}%{bold}%{b} %{p};") % {:u => user, :v => verb, :t => track, :a => artist, :b => album, :p => ago, :bold => Bold}
|
263
388
|
end
|
389
|
+
|
390
|
+
if Object.const_defined?('Spotify')
|
391
|
+
if track = Spotify.search(:track, "#{artist} #{track}")
|
392
|
+
reply << _(" [%{u}%{url}%{u}]") % {:u => Underline, :url => track.url}
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
reply << _(" -- see %{uri} for more") % { :uri => "http://www.last.fm/user/#{CGI.escape user}"}
|
397
|
+
m.reply reply
|
264
398
|
end
|
265
399
|
|
266
400
|
def find_artist(m, params)
|
267
|
-
|
268
|
-
unless
|
269
|
-
m.reply _("I had problems getting info for %{a}
|
401
|
+
info_xml = @bot.httputil.get("#{APIURL}method=artist.getinfo&artist=#{CGI.escape params[:artist].to_s}")
|
402
|
+
unless info_xml
|
403
|
+
m.reply _("I had problems getting info for %{a}") % {:a => params[:artist]}
|
270
404
|
return
|
271
405
|
end
|
272
|
-
|
273
|
-
unless
|
406
|
+
info_doc = Document.new info_xml
|
407
|
+
unless info_doc
|
274
408
|
m.reply _("last.fm parsing failed")
|
275
409
|
return
|
276
410
|
end
|
277
|
-
|
411
|
+
tags_xml = @bot.httputil.get("#{APIURL}method=artist.gettoptags&artist=#{CGI.escape params[:artist].to_s}")
|
412
|
+
tags_doc = Document.new tags_xml
|
413
|
+
|
414
|
+
first = info_doc.root.elements["artist"]
|
278
415
|
artist = first.elements["name"].text
|
279
|
-
|
280
|
-
|
416
|
+
url = first.elements["url"].text
|
417
|
+
stats = {}
|
418
|
+
%w(playcount listeners).each do |e|
|
419
|
+
t = first.elements["stats/#{e}"].text
|
420
|
+
stats[e.to_sym] = t.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
|
421
|
+
end
|
281
422
|
summary = first.elements["bio"].elements["summary"].text
|
282
|
-
|
423
|
+
similar = first.get_elements("similar/artist").map { |a|
|
424
|
+
_("%{b}%{a}%{b}") % { :a => a.elements["name"].text, :b => Bold } }
|
425
|
+
tags = tags_doc.root.get_elements("toptags/tag")[0..4].map { |t|
|
426
|
+
_("%{u}%{t}%{u}") % { :t => t.elements["name"].text, :u => Underline } }
|
427
|
+
reply = _("%{b}%{a}%{b} <%{u}> has been played %{b}%{c}%{b} times and is being listened to by %{b}%{l}%{b} people") % {
|
428
|
+
:b => Bold, :a => artist, :u => url, :c => stats[:playcount], :l => stats[:listeners] }
|
429
|
+
reply << _(". Tagged as: %{t}") % {
|
430
|
+
:t => tags.join(", "), :b => Bold } unless tags.empty?
|
431
|
+
reply << _(". Similar artists: %{s}") % {
|
432
|
+
:s => similar.join(", "), :b => Bold } unless similar.empty?
|
433
|
+
m.reply reply
|
283
434
|
m.reply summary.ircify_html
|
284
435
|
end
|
285
436
|
|
@@ -287,7 +438,7 @@ class LastFmPlugin < Plugin
|
|
287
438
|
track = params[:track].to_s
|
288
439
|
xml = @bot.httputil.get("#{APIURL}method=track.search&track=#{CGI.escape track}")
|
289
440
|
unless xml
|
290
|
-
m.reply _("I had problems getting info for %{a}
|
441
|
+
m.reply _("I had problems getting info for %{a}") % {:a => track}
|
291
442
|
return
|
292
443
|
end
|
293
444
|
debug xml
|
@@ -319,6 +470,44 @@ class LastFmPlugin < Plugin
|
|
319
470
|
end
|
320
471
|
end
|
321
472
|
|
473
|
+
def find_venue(m, params)
|
474
|
+
venue = params[:venue].to_s
|
475
|
+
venues = search_venue_by(:name => venue, :limit => 1)
|
476
|
+
venue = venues.last
|
477
|
+
|
478
|
+
if venues.empty?
|
479
|
+
m.reply "sorry, can't find such venue"
|
480
|
+
return
|
481
|
+
end
|
482
|
+
|
483
|
+
reply = _("%{b}%{name}%{b}, %{street}, %{u}%{city}%{u}, %{country}, see %{url} for more info") % {
|
484
|
+
:u => Underline, :b => Bold, :name => venue.name, :city => venue.city, :street => venue.street,
|
485
|
+
:country => venue.country, :url => venue.url
|
486
|
+
}
|
487
|
+
|
488
|
+
if venue.street && venue.city
|
489
|
+
maps_uri = "http://maps.google.com/maps?q=#{venue.street},+#{venue.city}"
|
490
|
+
maps_uri << ",+#{venue.postal}" if venue.postal
|
491
|
+
elsif venue.lat && venue.long
|
492
|
+
maps_uri = "http://maps.google.com/maps?q=#{venue.lat},+#{venue.long}"
|
493
|
+
else
|
494
|
+
m.reply reply
|
495
|
+
return
|
496
|
+
end
|
497
|
+
|
498
|
+
maps_uri << "+(#{venue.name.gsub(" ", "%A0")})"
|
499
|
+
|
500
|
+
begin
|
501
|
+
require "shorturl"
|
502
|
+
maps_uri = ShortURL.shorten(CGI.escape(maps_uri))
|
503
|
+
rescue LoadError => e
|
504
|
+
error e
|
505
|
+
end
|
506
|
+
|
507
|
+
reply << _(" and %{maps} for maps") % { :maps => maps_uri, :b => Bold }
|
508
|
+
m.reply reply
|
509
|
+
end
|
510
|
+
|
322
511
|
def get_album(artist, album)
|
323
512
|
xml = @bot.httputil.get("#{APIURL}method=album.getinfo&artist=#{CGI.escape artist}&album=#{CGI.escape album}")
|
324
513
|
unless xml
|
@@ -334,7 +523,7 @@ class LastFmPlugin < Plugin
|
|
334
523
|
playcount = first.elements["playcount"].text
|
335
524
|
album = first.elements["name"].text
|
336
525
|
date = first.elements["releasedate"].text
|
337
|
-
unless date.strip.length < 2
|
526
|
+
unless date.strip.length < 2
|
338
527
|
year = date.strip.split[2].chop
|
339
528
|
end
|
340
529
|
result = [artist, album, year, playcount]
|
@@ -348,14 +537,14 @@ class LastFmPlugin < Plugin
|
|
348
537
|
return
|
349
538
|
end
|
350
539
|
year = "(#{album[2]}) " unless album[2] == nil
|
351
|
-
m.reply _("
|
540
|
+
m.reply _("the album \"%{a}\" by %{r} %{y}has been played %{c} times") % {:a => album[1], :r => album[0], :y => year, :c => album[3]}
|
352
541
|
end
|
353
542
|
|
354
543
|
def set_user(m, params)
|
355
544
|
user = params[:who].to_s
|
356
545
|
nick = m.sourcenick
|
357
546
|
@registry[ nick ] = user
|
358
|
-
m.reply _("
|
547
|
+
m.reply _("okay, I'll remember that %{n} is %{u} on last.fm") % {:n => nick, :u => user}
|
359
548
|
end
|
360
549
|
|
361
550
|
def set_verb(m, params)
|
@@ -364,71 +553,245 @@ class LastFmPlugin < Plugin
|
|
364
553
|
key = "#{m.sourcenick}_verb_"
|
365
554
|
@registry[ "#{key}past" ] = past
|
366
555
|
@registry[ "#{key}present" ] = present
|
367
|
-
m.reply _("
|
556
|
+
m.reply _("okay, I'll remember that %{n} prefers \"%{r}\" and \"%{p}\"") % {:n => m.sourcenick, :p => past, :r => present}
|
368
557
|
end
|
369
558
|
|
370
559
|
def get_user(m, params)
|
371
560
|
nick = ""
|
372
561
|
if params[:who]
|
373
562
|
nick = params[:who].to_s
|
374
|
-
else
|
563
|
+
else
|
375
564
|
nick = m.sourcenick
|
376
565
|
end
|
377
566
|
if @registry.has_key? nick
|
378
567
|
user = @registry[ nick ]
|
379
|
-
m.reply "
|
568
|
+
m.reply _("%{nick} is %{user} on last.fm") % {
|
569
|
+
:nick => nick,
|
570
|
+
:user => user
|
571
|
+
}
|
380
572
|
else
|
381
|
-
m.reply _("
|
573
|
+
m.reply _("sorry, I don't know who %{n} is on last.fm, perhaps they need to: lastfm set user <username>") % {:n => nick}
|
382
574
|
end
|
383
575
|
end
|
384
576
|
|
385
|
-
# TODO this user data retrieval should be upgraded to API 2.0 but it would need separate parsing
|
386
|
-
# for each dataset, or almost
|
387
577
|
def lastfm(m, params)
|
388
|
-
action = params[:action]
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
578
|
+
action = case params[:action]
|
579
|
+
when "neighbors" then "neighbours"
|
580
|
+
when "recentracks", "recent" then "recenttracks"
|
581
|
+
when "loved" then "lovedtracks"
|
582
|
+
when /^weekly(track|album|artist)s$/
|
583
|
+
"weekly#{$1}chart"
|
584
|
+
when "events"
|
585
|
+
find_events(m, params)
|
586
|
+
return
|
587
|
+
else
|
588
|
+
params[:action]
|
589
|
+
end.to_sym
|
590
|
+
|
591
|
+
if action == :shouts
|
592
|
+
num = params[:num] || @bot.config['lastfm.default_shouts']
|
593
|
+
num = num.to_i.clip(1, @bot.config['lastfm.max_shouts'])
|
399
594
|
else
|
400
|
-
|
401
|
-
|
402
|
-
user = m.sourcenick
|
595
|
+
num = params[:num] || @bot.config['lastfm.default_user_data']
|
596
|
+
num = num.to_i.clip(1, @bot.config['lastfm.max_user_data'])
|
403
597
|
end
|
598
|
+
|
599
|
+
user = resolve_username(m, params[:user])
|
600
|
+
uri = "#{APIURL}method=user.get#{action}&user=#{CGI.escape user}"
|
601
|
+
|
602
|
+
if period = params[:period]
|
603
|
+
period_uri = (period.last == "year" ? "12month" : period.first + "month")
|
604
|
+
uri << "&period=#{period_uri}"
|
605
|
+
end
|
606
|
+
|
404
607
|
begin
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
608
|
+
res = @bot.httputil.get_response(uri)
|
609
|
+
raise _("no response body") unless res.body
|
610
|
+
rescue Exception => e
|
611
|
+
m.reply _("I had problems accessing last.fm: %{e}") % {:e => e.message}
|
612
|
+
return
|
613
|
+
end
|
614
|
+
doc = Document.new(res.body)
|
615
|
+
unless doc
|
616
|
+
m.reply _("last.fm parsing failed")
|
617
|
+
return
|
410
618
|
end
|
619
|
+
|
620
|
+
case res
|
621
|
+
when Net::HTTPBadRequest
|
622
|
+
if doc.root and doc.root.attributes["status"] == "failed"
|
623
|
+
m.reply "error: " << doc.root.elements["error"].text.downcase
|
624
|
+
end
|
625
|
+
return
|
626
|
+
end
|
627
|
+
|
628
|
+
seemore = _("; see %{uri} for more")
|
629
|
+
case action
|
630
|
+
when :friends
|
631
|
+
friends = doc.root.get_elements("friends/user").map do |u|
|
632
|
+
u.elements["name"].text
|
633
|
+
end
|
634
|
+
|
635
|
+
if friends.empty?
|
636
|
+
reply = _("%{user} has no friends :(")
|
637
|
+
elsif friends.length <= num
|
638
|
+
reply = _("%{user} has %{total} friends: %{friends}")
|
639
|
+
else
|
640
|
+
reply = _("%{user} has %{total} friends, including %{friends}%{seemore}")
|
641
|
+
end
|
642
|
+
m.reply reply % {
|
643
|
+
:user => user,
|
644
|
+
:total => friends.size,
|
645
|
+
:friends => Utils.comma_list(friends.shuffle[0, num]),
|
646
|
+
:seemore => seemore % { :uri => "http://www.last.fm/user/#{CGI.escape user}/friends" }
|
647
|
+
}
|
648
|
+
when :lovedtracks
|
649
|
+
loved = doc.root.get_elements("lovedtracks/track").map do |track|
|
650
|
+
[track.elements["artist/name"].text, track.elements["name"].text].join(" - ")
|
651
|
+
end
|
652
|
+
loved_prep = loved.shuffle[0, num].to_enum(:each_with_index).collect { |e,i| (i % 2).zero? ? Underline+e+Underline : e }
|
653
|
+
|
654
|
+
if loved.empty?
|
655
|
+
reply = _("%{user} has not loved any tracks")
|
656
|
+
elsif loved.length <= num
|
657
|
+
reply = _("%{user} has loved %{total} tracks: %{tracks}")
|
658
|
+
else
|
659
|
+
reply = _("%{user} has loved %{total} tracks, including %{tracks}%{seemore}")
|
660
|
+
end
|
661
|
+
|
662
|
+
m.reply reply % {
|
663
|
+
:user => user,
|
664
|
+
:total => loved.size,
|
665
|
+
:tracks => Utils.comma_list(loved_prep),
|
666
|
+
:seemore => seemore % { :uri => "http://www.last.fm/user/#{CGI.escape user}/library/loved" }
|
667
|
+
}
|
668
|
+
when :neighbours
|
669
|
+
nbrs = doc.root.get_elements("neighbours/user").map do |u|
|
670
|
+
u.elements["name"].text
|
671
|
+
end
|
672
|
+
|
673
|
+
if nbrs.empty?
|
674
|
+
reply = _("no one seems to share %{user}'s musical taste")
|
675
|
+
elsif nbrs.length <= num
|
676
|
+
reply = _("%{user}'s musical neighbours are %{nbrs}")
|
677
|
+
else
|
678
|
+
reply = _("%{user}'s musical neighbours include %{nbrs}%{seemore}")
|
679
|
+
end
|
680
|
+
m.reply reply % {
|
681
|
+
:user => user,
|
682
|
+
:nbrs => Utils.comma_list(nbrs.shuffle[0, num]),
|
683
|
+
:seemore => seemore % { :uri => "http://www.last.fm/user/#{CGI.escape user}/neighbours" }
|
684
|
+
}
|
685
|
+
when :recenttracks
|
686
|
+
tracks = doc.root.get_elements("recenttracks/track").map do |track|
|
687
|
+
[track.elements["artist"].text, track.elements["name"].text].join(" - ")
|
688
|
+
end
|
689
|
+
|
690
|
+
counts = []
|
691
|
+
tracks.each do |track|
|
692
|
+
if t = counts.assoc(track)
|
693
|
+
counts[counts.rindex(t)] = [track, t[-1] += 1]
|
694
|
+
else
|
695
|
+
counts << [track, 1]
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
tracks_prep = counts[0, num].to_enum(:each_with_index).map do |e,i|
|
700
|
+
str = (i % 2).zero? ? Underline+e[0]+Underline : e[0]
|
701
|
+
str << " (%{i} times%{m})" % {
|
702
|
+
:i => e.last,
|
703
|
+
:m => counts.size == 1 ? _(" or more") : nil
|
704
|
+
} if e.last > 1
|
705
|
+
str
|
706
|
+
end
|
707
|
+
|
708
|
+
if tracks.empty?
|
709
|
+
m.reply _("%{user} hasn't played anything recently") % { :user => user }
|
710
|
+
else
|
711
|
+
m.reply _("%{user} has recently played %{tracks}") %
|
712
|
+
{ :user => user, :tracks => Utils.comma_list(tracks_prep) }
|
713
|
+
end
|
714
|
+
when :shouts
|
715
|
+
shouts = doc.root.get_elements("shouts/shout")
|
716
|
+
if shouts.empty?
|
717
|
+
m.reply _("there are no shouts for %{user}") % { :user => user }
|
718
|
+
else
|
719
|
+
shouts[0, num].each do |shout|
|
720
|
+
m.reply _("<%{author}> %{body}") % {
|
721
|
+
:body => shout.elements["body"].text,
|
722
|
+
:author => shout.elements["author"].text,
|
723
|
+
}
|
724
|
+
end
|
725
|
+
end
|
726
|
+
when :toptracks, :topalbums, :topartists, :weeklytrackchart, :weeklyalbumchart, :weeklyartistchart
|
727
|
+
type = action.to_s.scan(/track|album|artist/).to_s
|
728
|
+
items = doc.root.get_elements("#{action}/#{type}").map do |item|
|
729
|
+
case action
|
730
|
+
when :weeklytrackchart, :weeklyalbumchart
|
731
|
+
format = "%{artist} - %{title} (%{bold}%{plays}%{bold})"
|
732
|
+
artist = item.elements["artist"].text
|
733
|
+
when :weeklyartistchart, :topartists
|
734
|
+
format = "%{artist} (%{bold}%{plays}%{bold})"
|
735
|
+
artist = item.elements["name"].text
|
736
|
+
when :toptracks, :topalbums
|
737
|
+
format = "%{artist} - %{title} (%{bold}%{plays}%{bold})"
|
738
|
+
artist = item.elements["artist/name"].text
|
739
|
+
end
|
740
|
+
|
741
|
+
_(format) % {
|
742
|
+
:artist => artist,
|
743
|
+
:title => item.elements["name"].text,
|
744
|
+
:plays => item.elements["playcount"].text,
|
745
|
+
:bold => Bold
|
746
|
+
}
|
747
|
+
end
|
748
|
+
if items.empty?
|
749
|
+
m.reply _("%{user} hasn't played anything in this period of time") % { :user => user }
|
750
|
+
else
|
751
|
+
m.reply items[0, num].join(", ")
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
def resolve_username(m, name)
|
757
|
+
name = m.sourcenick if name.nil?
|
758
|
+
@registry[name] or name
|
411
759
|
end
|
412
760
|
end
|
413
761
|
|
762
|
+
event_map_options = {
|
763
|
+
:action => :find_events,
|
764
|
+
:requirements => {
|
765
|
+
:num => /\d+/,
|
766
|
+
:sort_order => /(?:asc|desc)(?:ending)?/
|
767
|
+
},
|
768
|
+
:thread => true
|
769
|
+
}
|
770
|
+
|
414
771
|
plugin = LastFmPlugin.new
|
415
|
-
plugin.map 'lastfm [:num] event[s] in *location
|
416
|
-
plugin.map 'lastfm [:num] event[s] by *who
|
417
|
-
plugin.map 'lastfm [:num] event[s]
|
418
|
-
plugin.map 'lastfm
|
419
|
-
plugin.map 'lastfm now', :action => :now_playing, :thread => true
|
420
|
-
plugin.map 'np :who', :action => :now_playing, :thread => true
|
772
|
+
plugin.map 'lastfm [:num] event[s] in *location [sort[ed] by :sort_by] [[in] :sort_order [order]]', event_map_options.dup
|
773
|
+
plugin.map 'lastfm [:num] event[s] by *who [sort[ed] by :sort_by] [[in] :sort_order [order]]', event_map_options.dup
|
774
|
+
plugin.map 'lastfm [:num] event[s] at *venue [sort[ed] by :sort_by] [[in] :sort_order [order]]', event_map_options.dup
|
775
|
+
plugin.map 'lastfm [:num] event[s] [for] *who [sort[ed] by :sort_by] [[in] :sort_order [order]]', event_map_options.dup
|
421
776
|
plugin.map 'lastfm artist *artist', :action => :find_artist, :thread => true
|
422
777
|
plugin.map 'lastfm album *album [by *artist]', :action => :find_album
|
423
778
|
plugin.map 'lastfm track *track', :action => :find_track, :thread => true
|
424
|
-
plugin.map 'lastfm
|
425
|
-
plugin.map 'lastfm set
|
426
|
-
plugin.map 'lastfm
|
427
|
-
plugin.map 'lastfm who', :action => :get_user, :thread => true
|
428
|
-
plugin.map 'lastfm compare
|
429
|
-
plugin.map '
|
430
|
-
plugin.map "lastfm [user] :action [:user]", :thread => true,
|
431
|
-
:requirements => {
|
432
|
-
/^(?:events|friends|neighbou?rs|
|
779
|
+
plugin.map 'lastfm venue *venue', :action => :find_venue, :thread => true
|
780
|
+
plugin.map 'lastfm set user[name] :who', :action => :set_user, :thread => true
|
781
|
+
plugin.map 'lastfm set verb *present, *past', :action => :set_verb, :thread => true
|
782
|
+
plugin.map 'lastfm who [:who]', :action => :get_user, :thread => true
|
783
|
+
plugin.map 'lastfm compare to :user2', :action => :tasteometer, :thread => true
|
784
|
+
plugin.map 'lastfm compare [:user1] [to] :user2', :action => :tasteometer, :thread => true
|
785
|
+
plugin.map "lastfm [user] [:num] :action [:user]", :thread => true,
|
786
|
+
:requirements => {
|
787
|
+
:action => /^(?:events|shouts|friends|neighbou?rs|loved(?:tracks)?|recent(?:t?racks)?|top(?:album|artist|track)s?|weekly(?:albums?|artists?|tracks?)(?:chart)?)$/,
|
788
|
+
:num => /^\d+$/
|
789
|
+
}
|
790
|
+
plugin.map 'lastfm [user] [:num] :action [:user] over [*period]', :thread => true,
|
791
|
+
:requirements => {
|
792
|
+
:action => /^(?:top(?:album|artist|track)s?)$/,
|
793
|
+
:period => /^(?:(?:3|6|12) months)|(?:a\s|1\s)?year$/,
|
794
|
+
:num => /^\d+$/
|
433
795
|
}
|
434
|
-
plugin.map
|
796
|
+
plugin.map 'lastfm [now] [:who]', :action => :now_playing, :thread => true
|
797
|
+
plugin.map 'np [:who]', :action => :now_playing, :thread => true
|