zetabot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +281 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +41 -0
  7. data/Rakefile +6 -0
  8. data/Zeta.gemspec +74 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +13 -0
  11. data/bin/zeta +9 -0
  12. data/bin/zeta-setup +13 -0
  13. data/lib/Zeta.rb +13 -0
  14. data/lib/Zeta/access.rb +84 -0
  15. data/lib/Zeta/admin.rb +10 -0
  16. data/lib/Zeta/admin/autojoin.rb +25 -0
  17. data/lib/Zeta/admin/bot.rb +43 -0
  18. data/lib/Zeta/admin/channels.rb +36 -0
  19. data/lib/Zeta/admin/eval.rb +43 -0
  20. data/lib/Zeta/admin/fifo.rb +45 -0
  21. data/lib/Zeta/admin/ignore.rb +112 -0
  22. data/lib/Zeta/admin/oper.rb +50 -0
  23. data/lib/Zeta/admin/plugins.rb +109 -0
  24. data/lib/Zeta/admin/users.rb +5 -0
  25. data/lib/Zeta/blacklist.rb +25 -0
  26. data/lib/Zeta/cache.rb +0 -0
  27. data/lib/Zeta/cinch.rb +35 -0
  28. data/lib/Zeta/config.rb +42 -0
  29. data/lib/Zeta/gems.rb +0 -0
  30. data/lib/Zeta/locale.rb +4 -0
  31. data/lib/Zeta/log.rb +2 -0
  32. data/lib/Zeta/models.rb +5 -0
  33. data/lib/Zeta/models/channel.rb +3 -0
  34. data/lib/Zeta/models/plugin.rb +3 -0
  35. data/lib/Zeta/models/user.rb +3 -0
  36. data/lib/Zeta/plugins.rb +27 -0
  37. data/lib/Zeta/plugins/attack.rb +61 -0
  38. data/lib/Zeta/plugins/botinfo.rb +83 -0
  39. data/lib/Zeta/plugins/darkscience.rb +215 -0
  40. data/lib/Zeta/plugins/dbz.rb +31 -0
  41. data/lib/Zeta/plugins/dcc.rb +25 -0
  42. data/lib/Zeta/plugins/dnsbl.rb +36 -0
  43. data/lib/Zeta/plugins/echo.rb +15 -0
  44. data/lib/Zeta/plugins/eightball.rb +53 -0
  45. data/lib/Zeta/plugins/fml.rb +35 -0
  46. data/lib/Zeta/plugins/fnord.rb +329 -0
  47. data/lib/Zeta/plugins/gem.rb +0 -0
  48. data/lib/Zeta/plugins/gif.rb +73 -0
  49. data/lib/Zeta/plugins/help.rb +32 -0
  50. data/lib/Zeta/plugins/libsecure.rb +34 -0
  51. data/lib/Zeta/plugins/macros.rb +124 -0
  52. data/lib/Zeta/plugins/movie.rb +67 -0
  53. data/lib/Zeta/plugins/pdfinfo.rb +69 -0
  54. data/lib/Zeta/plugins/rainbow.rb +65 -0
  55. data/lib/Zeta/plugins/russian_roulette.rb +90 -0
  56. data/lib/Zeta/plugins/seen.rb +77 -0
  57. data/lib/Zeta/plugins/silly.rb +183 -0
  58. data/lib/Zeta/plugins/snooper.rb +146 -0
  59. data/lib/Zeta/plugins/urban.rb +60 -0
  60. data/lib/Zeta/plugins/weather.rb +203 -0
  61. data/lib/Zeta/plugins/whois.rb +25 -0
  62. data/lib/Zeta/plugins/wiki.rb +75 -0
  63. data/lib/Zeta/plugins/wolfram.rb +46 -0
  64. data/lib/Zeta/tasks/db.rb +0 -0
  65. data/lib/Zeta/version.rb +3 -0
  66. data/lib/generators/config/config.rb +0 -0
  67. data/lib/generators/plugin/new_plugin.rb +0 -0
  68. data/locale/en/8ball.yml +0 -0
  69. data/locale/en/attack.yml +148 -0
  70. data/locale/en/dbz.yml +10 -0
  71. data/locale/en/ircop.yml +5 -0
  72. data/locale/en/macros.yml +98 -0
  73. data/locale/en/meme.yml +27 -0
  74. metadata +636 -0
@@ -0,0 +1,90 @@
1
+ module Plugins
2
+ class RussianRoulette
3
+ include Cinch::Plugin
4
+ include Cinch::Helpers
5
+ enable_acl
6
+
7
+ set plugin_name: "Russian Roulette",
8
+ help: "In Soviet Russia, boolet shoots YOU!\nUsage: ?rr <nick>",
9
+ react_on: :channel
10
+
11
+ attr_reader :games
12
+
13
+ PHRASES = [
14
+ "\"BANG\" You're dead! ... Just kidding comrade... for now.",
15
+ "\"...BLAMMO!\" (hangfires are a bitch, aren't they?)",
16
+ "Are you scared yet, comrade?",
17
+ "Are you pissing your pants yet?",
18
+ "You are lucky, comrade. At least for now.",
19
+ "The chamber was as empty as your head.",
20
+ "Damn. And I thought that it had bullet too.",
21
+ "Lucky you, comrade.",
22
+ "Must be your lucky day, comrade.",
23
+ "\"BLAM!\" you can't play russian roulette with a tokarev, comrade.",
24
+ "... Looks like you get to live another day... or 5 second.",
25
+ "... Bad primer."
26
+ ]
27
+
28
+ def initialize(*args)
29
+ super
30
+ @games = []
31
+ end
32
+
33
+ match /rr(?: (.+))?/, method: :russian
34
+
35
+ def russian(m, nick)
36
+ #return m.reply "I am sorry comrade, but I do not have pistol on me." unless check_user(m.channel, @bot)
37
+ return m.user.notice "Sorry comrade, but there is already game going on." if @games.include?(m.channel.name)
38
+
39
+ # player setup
40
+ player = User(nick) || m.user
41
+ player = m.user if player == @bot
42
+ # be nice, don't force the game on the starter unless the user actually exists in the channel.
43
+ return m.reply "I am terribly sorry %s, but I don't know %s." % [m.user.nick, player.nick] unless m.channel.users.include?(player)
44
+
45
+ # start game
46
+ @games << m.channel.name
47
+
48
+ turns, round_location = Array.new(2) { |i| Random.new.rand(1..6) }
49
+ m.channel.action "starts a %d-turn game of Russian Roulette with %s." % [turns, player.nick]
50
+
51
+ phrases = PHRASES.dup.shuffle
52
+
53
+ sleep 5
54
+
55
+ turns.times do |chamber|
56
+ return end_game(m.channel, true) unless m.channel.users.include?(player)
57
+ if round_location == chamber.succ
58
+ m.reply "*click*"
59
+ sleep 5
60
+ m.channel.kick(player, "*BLAM*")
61
+ m.reply "*BLAM*"
62
+ m.channel.action "watches %s's brain splatter across the wall." % player.nick
63
+ break
64
+ else
65
+ phrase = phrases.pop
66
+ m.reply "*click* %s" % phrase
67
+ end
68
+ sleep 5
69
+ end
70
+
71
+ m.reply "Looks like you get to live another day." if turns < round_location
72
+ sleep 1 if turns < round_location
73
+ end_game(m.channel)
74
+ end
75
+
76
+ private
77
+
78
+ def end_game(channel, premature=false)
79
+ @games.delete channel.name
80
+ channel.msg "Oh vell, it vas fun vhile it lasted." if premature
81
+ sleep 1
82
+ channel.action "holsters the pistol."
83
+ end
84
+ end
85
+ end
86
+
87
+
88
+ # AutoLoad
89
+ Bot.config.plugins.plugins.push Plugins::RussianRoulette
90
+
@@ -0,0 +1,77 @@
1
+ require 'action_view'
2
+ module Plugins
3
+ class Seen
4
+ class SeenStruct < Struct.new(:who, :where, :what, :time)
5
+ include ActionView::Helpers::DateHelper
6
+ def to_s
7
+ # "[#{time.asctime}] #{who} was seen in #{where} last saying #{what}"
8
+ time_ago = time_ago_in_words(Time.at(time))
9
+ "[ \x1F#{where.to_s.upcase}\x0F ] \x0304#{who}\x0F: \"\x0303#{what[0..300]}\x0F\" \x02#{time_ago}\x0F ago"
10
+ end
11
+ end
12
+
13
+ include Cinch::Plugin
14
+ include Cinch::Helpers
15
+
16
+ enable_acl(:nobody)
17
+
18
+ listen_to :channel
19
+ match /seen (.+)/
20
+ match /sync/, method: :sync
21
+
22
+ def initialize(*args)
23
+ super
24
+ @users = load_seen
25
+ end
26
+
27
+ def finalize
28
+ save_seen()
29
+ end
30
+
31
+ def listen(m)
32
+ @users[m.user.nick] = SeenStruct.new(m.user, m.channel, m.message, Time.now)
33
+ end
34
+
35
+ def execute(m, nick)
36
+ nick.rstrip!
37
+ if nick == @bot.nick
38
+ m.reply 'You are a Stupid human!'
39
+ elsif nick == m.user.nick
40
+ m.reply "Unfortunately, I see an idiot by the name of #{m.user.nick}"
41
+ elsif @users.key?(nick)
42
+ m.reply @users[nick].to_s
43
+ else
44
+ m.reply "I haven't seen #{nick}"
45
+ end
46
+ end
47
+
48
+ def sync(m)
49
+ save_seen()
50
+ end
51
+
52
+ def save_seen
53
+ File.open(File.join(Dir.home, '.zeta', 'cache', 'seen.rb'), 'w+') do |file|
54
+ Marshal.dump(@users, file)
55
+ end
56
+
57
+ end
58
+
59
+ def load_seen
60
+ if File.exists?(File.join(Dir.home, '.zeta', 'cache', 'seen.rb'))
61
+ File.open(File.join(Dir.home, '.zeta', 'cache', 'seen.rb')) do |file|
62
+ return Marshal.load(file)
63
+ end
64
+ else
65
+ return Hash.new
66
+ end
67
+ end
68
+
69
+ def clear_seen
70
+ @users = {}
71
+ File.delete(File.join(Dir.home, '.zeta', 'cache', 'seen.rb'))
72
+ end
73
+ end
74
+ end
75
+
76
+ # AutoLoad
77
+ Bot.config.plugins.plugins.push Plugins::Seen
@@ -0,0 +1,183 @@
1
+ # coding: utf-8
2
+
3
+ require 'active_support/time'
4
+ require 'active_support/core_ext/string'
5
+ require 'active_support/core_ext/object/blank'
6
+ require 'chronic_duration'
7
+ require 'date'
8
+
9
+
10
+ module Plugins
11
+ class Silly
12
+ include Cinch::Plugin
13
+ include Cinch::Helpers
14
+ enable_acl
15
+
16
+ set(
17
+ plugin_name: "Silly",
18
+ help: "You know, silly stuff.",
19
+ )
20
+
21
+ _seconds_in_a_day = 86400
22
+
23
+ attr_reader :pokers
24
+
25
+ def initialize *args
26
+ super
27
+ @pokers = {}
28
+ end
29
+
30
+ match /^pokes (.+)$/, react_on: :action, method: :listen_poke, use_prefix: false
31
+
32
+ def listen_poke(m, thebot)
33
+
34
+ if User(thebot) == @bot
35
+ @pokers[m.user] = 0 if !@pokers.include?(m.user)
36
+ @pokers[m.user] += 1
37
+ case @pokers[m.user]
38
+ when 1..3
39
+ m.reply "Do NOT poke the bot!"
40
+ when 4
41
+ m.reply "I said, do NOT poke the bot!"
42
+ when 5
43
+ msg = ["WHAT ARE YOU, AN IDIOT? I SAID DO NOT POKE ME!!", "THIS! IS! SPARTA!!"].sample
44
+ m.channel.kick m.user, ["WHAT ARE YOU, AN IDIOT? I SAID DO NOT POKE ME!!", "THIS! IS! SPARTA!!"].sample
45
+ @pokers.delete(m.user)
46
+ end
47
+ end
48
+ end
49
+
50
+ match /\b(dumb|stupid)\b.+\bbots*\b/i, method: :execute_botinsult, use_prefix: false
51
+
52
+ def execute_botinsult(m)
53
+
54
+ m.reply ["Stupid human!", "Dumb human!", "Stupid meatbag.", "Silly human, your insults cannot harm me!", "get rekt", "u fkn w0t m8"].sample
55
+ end
56
+
57
+ def tzparser(tz)
58
+ prefix = (tz[0] !~ /(\+|-)/ ? "+" : "")
59
+ suffix = (tz =~ /^(?:\+|-)?(\d{1,2})$/ ? ":00" : "")
60
+ regexp = /^(\+|-)?(\d{1,2})(?::(\d{1,2}))?$/
61
+ if tz =~ regexp
62
+ prefix + tz.gsub(regexp) { |match| (!!$1 ? $1 : "") + $2.rjust(2, "0") + (!!$3 ? ":"+$3.rjust(2, "0") : "") } + suffix
63
+ else
64
+ raise ArgumentError, "A valid timezone was not supplied."
65
+ end
66
+ end
67
+
68
+ match "halloween", method: :halloween
69
+ match "halloweve", method: :halloween
70
+ match /halloween (\S+)/, method: :halloween
71
+
72
+ def halloween(m, tz = nil)
73
+ tz ||= "-00:00"
74
+ tz = tzparser(tz)
75
+ begin
76
+ today = Time.now.localtime(tz)
77
+ hw = Time.new(today.year, 10, 31, 0, 0, 0, tz)
78
+ hw = hw.next_year if hw.to_date.past?
79
+ message = if hw.to_date == today.to_date
80
+ "THIS IS HALLOWEEN!"
81
+ else
82
+ "There's #{ChronicDuration.output(hw.to_i - today.to_i, format: :long)} until Hallow's Eve!"
83
+ end
84
+ rescue ArgumentError => ae
85
+ message = ae.message
86
+ ensure
87
+ m.reply message, true
88
+ end
89
+ end
90
+
91
+ match "xmas", method: :xmas
92
+ match /xmas (\S+)/, method: :xmas
93
+
94
+ def xmas(m, tz = nil)
95
+ tz ||= "-00:00"
96
+ tz = tzparser(tz)
97
+ begin
98
+ today = Time.now.localtime(tz)
99
+ xmas = Time.new(today.year, 12, 25, 0, 0, 0, tz)
100
+ xmas = xmas.next_year if xmas.to_date.past?
101
+ message = if xmas.to_date == today.to_date
102
+ "Merry Christmas!"
103
+ else
104
+ "There's #{ChronicDuration.output(xmas.to_i - today.to_i, format: :long)} until Christmas!"
105
+ end
106
+ rescue ArgumentError => ae
107
+ message = ae.message
108
+ ensure
109
+ m.reply message, true
110
+ end
111
+ end
112
+
113
+ match "newyear", method: :newyear
114
+ match /newyear (\S+)/, method: :newyear
115
+
116
+ def newyear(m, tz = nil)
117
+ tz ||= "-00:00"
118
+ tz = tzparser(tz)
119
+ begin
120
+ today = Time.now.localtime(tz)
121
+ nyear = Time.new(today.year.succ, 1, 1, 0, 0, 0, tz)
122
+ #nyear = nyear.next_year if nyear.to_date.past?
123
+ message = if nyear.to_date == today.to_date
124
+ "Happy New Year #{today.year}!"
125
+ else
126
+ "There's #{ChronicDuration.output(nyear.to_i - today.to_i, format: :long)} until #{nyear.year}!"
127
+ end
128
+ rescue ArgumentError => ae
129
+ message = ae.message
130
+ ensure
131
+ m.reply message, true
132
+ end
133
+ end
134
+
135
+ match "tz", method: :tz
136
+ match /tz (\S+)/, method: :tz
137
+
138
+ def tz(m, tz = nil)
139
+ tz ||= "-00:00"
140
+ tz = tzparser(tz)
141
+ begin
142
+ today = Time.now.localtime(tz)
143
+ message = today.to_s
144
+ rescue ArgumentError => ae
145
+ message = ae.message
146
+ ensure
147
+ m.reply message, true
148
+ end
149
+ end
150
+
151
+ match "mayan", method: :mayan
152
+
153
+ def mayan(m)
154
+ msd = (Date.today.jd - Date.new(1, 1, 1).jd) + 1137142
155
+ lc = {
156
+ baktun: (msd - (msd % 144000)) / 144000,
157
+ katun: ((msd - (msd % 7200)) / 7200) % 20,
158
+ tun: ((msd - (msd % 360)) / 360) % 20,
159
+ uinal: ((msd - (msd % 20)) / 20) % 18,
160
+ kin: (msd % 20)
161
+ }
162
+
163
+ m.reply '%<baktun>s.%<katun>s.%<tun>s.%<uinal>s.%<kin>s' % lc
164
+ end
165
+
166
+ match /heavymetalize (.+)/, method: :heavymetalize
167
+
168
+ def heavymetalize(m, s)
169
+ m.reply s.tr('AEIOUaeiouyYWwXx', 'ÄËÏÖÜäëïöüÿŸẄẅẌẍ')
170
+ end
171
+
172
+ match /\bfeature\b.+\brequest\b/i, method: :execute_userproblem, use_prefix: false
173
+
174
+ def execute_userproblem(m)
175
+ m.reply ["user error.", "working as intended", "Status: WONTFIX", "PEBKAC issue", "ID:10T Error"].sample
176
+ end
177
+
178
+ end
179
+ end
180
+
181
+
182
+ # AutoLoad
183
+ Bot.config.plugins.plugins.push Plugins::Silly
@@ -0,0 +1,146 @@
1
+ # -*- coding: utf-8 -*-
2
+ # == Author
3
+ # Marvin Gülker (Quintus)
4
+ #
5
+ # == Modification author
6
+ # Liothen
7
+ #
8
+ # == License
9
+ # A named-pipe plugin for Cinch.
10
+ # Copyright © 2012 Marvin Gülker
11
+ #
12
+ # This program is free software: you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation, either version 3 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # This program is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+
25
+ # Plugin for inspecting links pasted into channels.
26
+ require 'video_info'
27
+ require 'mechanize'
28
+ require 'action_view'
29
+
30
+ module Plugins
31
+ class Snooper
32
+ include Cinch::Plugin
33
+ include Cinch::Helpers
34
+ include ActionView::Helpers::DateHelper
35
+
36
+ enable_acl(:nobody)
37
+
38
+ # Default list of URL regexps to ignore.
39
+ # DEFAULT_BLACKLIST = [/\.png$/i, /\.jpe?g$/i, /\.bmp$/i, /\.gif$/i, /\.pdf$/i].freeze
40
+
41
+ set :help, <<-HELP
42
+ http[s]://...
43
+ I’ll fire a GET request at any link I encounter, parse the HTML
44
+ meta tags, and paste the result back into the channel.
45
+ HELP
46
+
47
+ match %r{\b((https?:\/\/)?(([0-9a-zA-Z_!~*'().&=+$%-]+:)?[0-9a-zA-Z_!~*'().&=+$%-]+\@)?(([0-9]{1,3}\.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+\.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z]\.[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/[0-9a-zA-Z_!~*'().;?:\@&=+$,%#-]+)*\/?))}i,
48
+ use_prefix: false,
49
+ react_on: :channel
50
+
51
+ def execute(msg, url)
52
+ url = "http://#{url}" unless url=~/^https?:\/\//
53
+ url = URI.encode(url)
54
+
55
+ # Ignore items on blacklist
56
+ # blacklist = DEFAULT_BLACKLIST.dup
57
+ # blacklist.concat(config[:blacklist]) if config[:blacklist]
58
+ # return if blacklist.any?{|entry| url =~ entry}
59
+
60
+ # Log
61
+ # debug "URL matched: #{url}"
62
+
63
+ # Parse URI
64
+ p = URI(url)
65
+
66
+ # API key lookup
67
+ VideoInfo.provider_api_keys = { youtube: Config.secrets[:google] }
68
+
69
+ # Parse out specific websites
70
+ if p.host == 'youtube.com' || p.host == 'www.youtube.com' || p.host == 'youtu.be'
71
+ match_youtube(msg, url)
72
+ elsif p.host == 'i.imgur.com' || p.host == 'imgur.com'
73
+ match_imgur(msg, url)
74
+ else
75
+ match_other(msg,url)
76
+ end
77
+
78
+ rescue => e
79
+ error "#{e.class.name}: #{e.message}"
80
+ error "[404] #{msg.user} - #{url}"
81
+ end
82
+
83
+ private
84
+ def match_youtube(msg, url)
85
+ if Config.secrets[:google]
86
+ video = VideoInfo.new(url)
87
+ msg.reply "#{Format(:red, 'YouTube ')}∴ #{video.title} ( #{Format(:green, Time.at(video.duration).strftime("%H:%M:%S"))} )"
88
+ else
89
+ match_other(msg, url)
90
+ end
91
+
92
+ end
93
+
94
+ def match_github(msg, url)
95
+ # TODO parse github url
96
+ end
97
+
98
+ def match_imgur(msg, url)
99
+ id = url.to_s.match(%r{(?:https?://i?\.?imgur.com/)(?:.*\/)?([A-Za-z0-9]+)(?:\.jpg|png|gif|gifv)?}i)
100
+ return match_other(msg, url) unless id
101
+
102
+ # Query Data
103
+ data = JSON.parse(
104
+ RestClient.get("https://api.imgur.com/3/image/#{id[1]}", { Authorization: "Client-ID #{Config.secrets[:imgur_id]}" })
105
+ )
106
+ return 'Unable to query imgur' unless defined?(data)
107
+ i = Hashie::Mash.new(data)
108
+
109
+ # SET Not Safe For Work
110
+ if i.data.nsfw
111
+ nsf = "[#{Format(:red, 'NSFW')}]"
112
+ else
113
+ nsf = "[#{Format(:green, 'SAFE')}]"
114
+ end
115
+
116
+ # Trigger reply message
117
+ msg.reply("#{Format(:purple, 'IMGUR')} #{nsf} ∴ [#{Format(:yellow, i.data.type)}] #{i.data.width}x#{i.data.height} "\
118
+ "∴ Views: #{ i.data.views.to_s} ∴ #{i.data.title ? i.data.title[0..100] : 'No Title'} "\
119
+ "∴ Posted #{time_ago_in_words(Time.at(i.data.datetime))} ago")
120
+ end
121
+
122
+ def match_other(msg,url)
123
+ begin
124
+ html = Mechanize.start { |m| Nokogiri::HTML(m.get(url).content, nil, 'utf-8') }
125
+ if node = html.at_xpath("html/head/title")
126
+ msg.reply("‡ #{node.text.lstrip.gsub(/\r|\n|\n\r/, ' ')[0..300]}")
127
+ end
128
+
129
+ if node = html.at_xpath('html/head/meta[@name="description"]')
130
+ msg.reply("» #{node[:content].lines.first(3).join.gsub(/\r|\n|\n\r/, ' ')[0..300]}")
131
+ end
132
+
133
+ info "[200] #{msg.user} - #{url}"
134
+ rescue => e
135
+ error e
136
+ error "[404] #{msg.user} - #{url}"
137
+ end
138
+ end
139
+
140
+ end
141
+ end
142
+
143
+
144
+ # AutoLoad
145
+ Bot.config.plugins.plugins.push Plugins::Snooper
146
+