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,215 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'ostruct'
4
+ require 'action_view'
5
+
6
+ module Plugins
7
+ class DarkScience
8
+ include Cinch::Plugin
9
+ include Cinch::Helpers
10
+ include ActionView::Helpers::DateHelper
11
+
12
+ enable_acl(:nobody)
13
+
14
+ self.plugin_name = 'DarkScience API'
15
+ self.help = '?finger <nick>, ?peek <#channel>, ?stats <nick>, ?quote, ?addquote <quote>, ?quote <id>'
16
+
17
+ # Triggers
18
+ match /peek (.+)/, method: :peek
19
+ match /finger (.+)/, method: :finger
20
+ match /stats (.+)/, method: :stats
21
+ match /addquote (.+)/i, method: :addquote
22
+ match /quote (.+)/i, method: :quote
23
+ match 'quote', method: :randomquote
24
+
25
+
26
+ # Methods
27
+ #########
28
+ def peek(msg, channel)
29
+ chan = channel || msg.user.channel
30
+
31
+ # JSON Request
32
+ begin
33
+ RestClient.proxy = ENV['http_proxy']
34
+ data = JSON.parse(
35
+ RestClient.post(
36
+ 'https://darchoods.net/api/irc/channel/view',
37
+ {
38
+ auth_token: Config.secrets[:darkscience],
39
+ channel: chan,
40
+ }
41
+ )
42
+ )
43
+ rescue RestClient::Unauthorized
44
+ msg.action_reply "isn't currently authorized to do that"
45
+ end
46
+
47
+ # Turn JSON into an object
48
+ request = Hashie::Mash.new(data)
49
+
50
+ # Error Code replies
51
+ return msg.reply("Peek → #{request.message}") if request.status_code == 500
52
+ return msg.reply('Peek → Service Down') if request.status_code != 200
53
+ return msg.reply('Peek → Channel Not Found') if request.data.channel.empty?
54
+
55
+ msg.reply "Peek → #{request.data.channel.name} (#{request.data.channel.modes}) ~ " \
56
+ "Users: #{request.data.channel.stats.current_users} (#{request.data.channel.stats.peak_users}x̄) ~ " \
57
+ "Last Topic set by #{request.data.channel.topic.author} @ #{Time.at(request.data.channel.topic.time).strftime("%D")}"
58
+ end
59
+
60
+
61
+ def finger(msg, nickname)
62
+ nick = nickname || msg.user.nick
63
+
64
+ # JSON request
65
+ begin
66
+ RestClient.proxy = ENV['http_proxy']
67
+ data = JSON.parse(
68
+ RestClient.post(
69
+ 'https://darchoods.net/api/irc/user/view',
70
+ {
71
+ auth_token: Config.secrets[:darkscience],
72
+ username: nick,
73
+ }
74
+ )
75
+ )
76
+ rescue RestClient::Unauthorized
77
+ msg.action_reply "isn't currently authorized to do that"
78
+ end
79
+
80
+ # Turn JSON into an object
81
+ # request = Hashie::Mash.new(data)
82
+
83
+
84
+ # Error code replies
85
+ return msg.reply('Finger → User Not Found') if data['data']['user'].empty?
86
+ return msg.reply('Finger → Service Down') if data['status_code'] != 200
87
+
88
+ user = data['data']['user']
89
+ stats = data['data']['stats']
90
+ away_msg = data['data']['user']['away_msg'] || "No Message"
91
+ online_last = data['data']['user']['online_last'] || 0
92
+
93
+ msg.reply "Finger → #{user['userstring']} ~ " \
94
+ "#{user['identified'] ? 'Identified' : 'Not Identified'} ~ " \
95
+ "Currently in #{stats['channel_count']} channels ~ " \
96
+ "Seen #{user['online'] ? 'Now' : time_ago_in_words(Time.at(online_last))+ " ago"} ~ " \
97
+ "Geo: #{user['country']} ~ " \
98
+ "#{user['away'] ? 'Away: ' + away_msg : 'Available' } ~ " \
99
+ "Client: #{user['version']} ~ "
100
+ end
101
+
102
+
103
+ def stats(msg, nickname)
104
+ nick = nickname || msg.user.nick
105
+
106
+ # JSON request
107
+ begin
108
+ data = JSON.parse(
109
+ RestClient.post(
110
+ 'https://darchoods.net/api/irc/user/view',
111
+ {
112
+ auth_token: Config.secrets[:darkscience],
113
+ username: nick,
114
+ }
115
+ )
116
+ )
117
+ rescue RestClient::Unauthorized
118
+ msg.action_reply "isn't currently authorized to do that"
119
+ end
120
+
121
+ # Turn JSON into an object
122
+ # request = Hashie::Mash.new(data)
123
+
124
+
125
+ # Error code replies
126
+ return msg.reply('Statistics → User Not Found') if data['data']['user'].empty?
127
+ return msg.reply('Statistics → Service Down') if data['status_code'] != 200
128
+
129
+ user = data['data']['user']
130
+ stats = data['data']['stats']
131
+
132
+ msg.reply "Statistics → #{user['nick']} ~ " \
133
+ "Currently in #{stats['channel_count']} ~ " \
134
+ "Owner of #{stats['mode_counts']['q']} channels ~ " \
135
+ "Admin of #{stats['mode_counts']['a']} channels ~ " \
136
+ "Operator(halfop) of #{stats['mode_counts']['o']}(#{stats['mode_counts']['h']}) channels ~ " \
137
+ "and finally voiced in #{stats['mode_counts']['v']} channels"
138
+
139
+ end
140
+
141
+ def addquote(m, quote)
142
+ begin
143
+ request = JSON.parse(
144
+ RestClient.post(
145
+ 'https://darchoods.net/api/qdb/create',
146
+ {
147
+ auth_token: Config.secrets[:darkscience],
148
+ channel: m.channel,
149
+ author: m.user,
150
+ quote: quote
151
+ }
152
+ )
153
+ )
154
+ quote = Hashie::Mash.new(request)
155
+
156
+ m.reply "Quote ##{quote.data.quote.quote_id} added by #{m.user}!"
157
+ rescue RestClient::Unauthorized
158
+ m.action_reply "isn't currently authorized to do that"
159
+ rescue
160
+ m.reply 'QDB is unavailable right now'
161
+ end
162
+ end
163
+
164
+ def quote(m, search)
165
+ begin
166
+ request = JSON.parse(
167
+ RestClient.post(
168
+ 'https://darchoods.net/api/qdb/search/byId',
169
+ {
170
+ auth_token: Config.secrets[:darkscience],
171
+ channel: m.channel,
172
+ quote_id: search
173
+ }
174
+ )
175
+ )
176
+ quote = Hashie::Mash.new(request)
177
+
178
+ return m.reply 'There is no quote by that ID' unless quote.data.quote
179
+
180
+ m.reply "QDB##{quote.data.quote.quote_id}: #{quote.data.quote.content}"
181
+ rescue RestClient::Unauthorized
182
+ m.action_reply "isn't currently authorized to do that"
183
+ rescue
184
+ m.reply "QDB is unavailable right now"
185
+ end
186
+
187
+ end
188
+
189
+ def randomquote(m)
190
+ begin
191
+ request = JSON.parse(
192
+ RestClient.post(
193
+ 'https://darchoods.net/api/qdb/random',
194
+ { auth_token: Config.secrets[:darkscience],
195
+ channel: m.channel
196
+ }
197
+ )
198
+ )
199
+ quote = Hashie::Mash.new(request)
200
+
201
+ m.reply "QDB##{quote.data.quote.quote_id}: #{quote.data.quote.content}"
202
+ rescue RestClient::Unauthorized
203
+ m.action_reply "isn't currently authorized to do that"
204
+ rescue
205
+ m.reply "QDB is unavailable right now"
206
+ end
207
+
208
+ end
209
+
210
+
211
+ end
212
+ end
213
+
214
+ # AutoLoad
215
+ Bot.config.plugins.plugins.push Plugins::DarkScience
@@ -0,0 +1,31 @@
1
+ module Plugins
2
+ class DBZ
3
+ include Cinch::Plugin
4
+ include Cinch::Helpers
5
+
6
+ enable_acl
7
+
8
+ self.plugin_name = 'DragonBall Z!'
9
+ self.help = "It's Over Nine Thousand!"
10
+
11
+ # Regex
12
+ match /(^9000$|overninethousand|ninethousand|Over\ Nine\ Thousand|Over\ 9000)/,
13
+ use_prefix: false, method: :randomquote
14
+
15
+ # Initialization
16
+ def initialize(*args)
17
+ @sample = load_locale('dbz')
18
+ super
19
+ end
20
+
21
+ # Methods
22
+ def randomquote(msg)
23
+ msg.reply @sample['over9k'].sample
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ # AutoLoad
31
+ Bot.config.plugins.plugins.push Plugins::DBZ
@@ -0,0 +1,25 @@
1
+ # module Plugins
2
+ # class DCC
3
+ # include Cinch::Plugin
4
+ # set(
5
+ # plugin_name: "DCC",
6
+ # help: "Darkscience Code Contest.\nUsage: `~nick [channel]`;",
7
+ # prefix: /^!/
8
+ # )
9
+ # match /dcc/, method: :dcc
10
+ # match /dcc list/, method: :dcc_list
11
+ #
12
+ # def dcc
13
+ #
14
+ # end
15
+ #
16
+ # private
17
+ # def github()
18
+ # github = Github.new oauth_token: Zsec.keys.github
19
+ # end
20
+ #
21
+ # end
22
+ # end
23
+ #
24
+ # # AutoLoad
25
+ # Bot.config.plugins.plugins.push Plugins::DCC
@@ -0,0 +1,36 @@
1
+ require 'dnsbl/client'
2
+
3
+ module Plugins
4
+ class DNSBL
5
+ include Cinch::Plugin
6
+ include Cinch::Helpers
7
+
8
+ enable_acl
9
+
10
+ self.plugin_name = 'DNS Blacklist'
11
+ self.help = '?dnsbl <host>'
12
+
13
+ # Regex
14
+ match /dnsbl (.+)/, method: :dnsbl_lookup
15
+ match /blacklist (.+)/, method: :dnsbl_lookup
16
+
17
+ # Methods
18
+ def dnsbl_lookup(m, host)
19
+ client = DNSBL::Client.new
20
+ query = client.lookup(host.rstrip)
21
+
22
+ if query.empty?
23
+ m.reply "No Results Found (#{host})"
24
+ elsif query.last.dnsbl == 'URIBL' && query.last.meaning == '127.0.0.1'
25
+ m.reply "No Results Found (#{host})"
26
+ else
27
+ m.reply "Listed ⁘ #{host} ⁜ #{query.last.meaning} ⁜ Hits: #{query.count}"
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ # AutoLoad
36
+ Bot.config.plugins.plugins.push Plugins::DNSBL
@@ -0,0 +1,15 @@
1
+ module Plugins
2
+ class Echo
3
+ include Cinch::Plugin
4
+
5
+ enable_acl(:voice)
6
+
7
+ match /echo (.+)/, method: :echoed
8
+
9
+ def echoed(m,s)
10
+ m.reply s
11
+ end
12
+
13
+ end
14
+ end
15
+ Bot.config.plugins.plugins.push Plugins::Echo
@@ -0,0 +1,53 @@
1
+ module Plugins
2
+ class Eightball
3
+ include Cinch::Plugin
4
+ include Cinch::Helpers
5
+
6
+ enable_acl
7
+
8
+ set(
9
+ plugin_name: "8ball",
10
+ help: "The Magic 8ball has all the answers!\nUsage: `?8ball [question? <question? <...>>]`",
11
+ react_on: :channel)
12
+
13
+ # Variables
14
+ @@eightball = [
15
+ "It is certain",
16
+ "It is decidedly so",
17
+ "Without a doubt",
18
+ "Yes - definitely",
19
+ "You may rely on it",
20
+ "As I see it, yes",
21
+ "Most likely",
22
+ "Outlook good",
23
+ "Signs point to yes",
24
+ "Yes",
25
+ "Reply hazy, try again",
26
+ "Ask again later",
27
+ "Better not tell you now",
28
+ "Cannot predict now",
29
+ "Concentrate and ask again",
30
+ "Don't count on it",
31
+ "My reply is no",
32
+ "My sources say no",
33
+ "Outlook not so good",
34
+ "Very doubtful"
35
+ ]
36
+
37
+ # Regex
38
+ match /8ball (.+)/
39
+
40
+ # Methods
41
+ def shake!
42
+ @@eightball.sample
43
+ end
44
+
45
+ def execute(m, s)
46
+ m.safe_reply @@eightball.sample, true
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ # AutoLoad
53
+ Bot.config.plugins.plugins.push Plugins::Eightball
@@ -0,0 +1,35 @@
1
+ # This plugin is brought to you by aburdette/cinch-fmylife
2
+ # Thank You!
3
+
4
+ module Plugins
5
+ class Fml
6
+ include Cinch::Plugin
7
+ include Cinch::Helpers
8
+
9
+ enable_acl(:nobody)
10
+
11
+ set(
12
+ plugin_name: "FuckMyLife",
13
+ help: "Get a random FML.\nUsage: `?fml`",
14
+ )
15
+
16
+ match /fml/
17
+
18
+ def execute(m)
19
+ m.reply fetch_random_fml, true
20
+ end
21
+
22
+ private
23
+ def fetch_random_fml
24
+ url = 'http://www.fmylife.com/random'
25
+ # fml_story = Nokogiri.HTML(RestClient.get(url)).at('div.article').text.strip
26
+ fml_story = Nokogiri.HTML(open(url).read).at('div.article').text.strip
27
+ fml_story[/^Today, (.+) FML/]
28
+ rescue => e
29
+ e.message
30
+ end
31
+ end
32
+ end
33
+
34
+ # AutoLoad
35
+ Bot.config.plugins.plugins.push Plugins::Fml
@@ -0,0 +1,329 @@
1
+ require 'cinch'
2
+ require 'chronic'
3
+
4
+ module Plugins
5
+ class Fnord
6
+ include Cinch::Plugin
7
+ include Cinch::Helpers
8
+ enable_acl
9
+
10
+ @help="fnord: ?fnord - Creates a fnord"
11
+ @plugin_name="fnord"
12
+ ADJECTIVES =
13
+ ["23rd", "acceptable", "acrobatic", "alien", "amiable",
14
+ "amoeboid", "ancient", "arbitrary", "atomic", "avenging",
15
+ "awesome", "balanced", "besotted", "best", "black", "blue",
16
+ "calculating", "cast iron", "cat-like", "cautious", "Chinese",
17
+ "cold", "communist", "corrupt", "dead", "deadly", "dehydrated",
18
+ "disguised", "dizzy", "drug-crazed", "drunken", "easy",
19
+ "electric", "embossed", "enormous", "expensive", "explosive",
20
+ "extraterrestrial", "ferocious", "frozen", "furry", "gelatinous",
21
+ "glowing", "gnarly", "gold", "granular", "greedy", "green",
22
+ "high", "highest", "hot", "humming", "illuminated", "imitation",
23
+ "impotent", "impudent", "impulsive", "indictable", "innocent",
24
+ "insane", "Japanese", "lecherous", "lizard-like", "lovely",
25
+ "maniacal", "mauve", "medium-sized", "morbid", "most influential",
26
+ "mutant", "naughty", "nuclear", "oily", "oozing", "opaque",
27
+ "opulent", "orbital", "persuasive", "pickled", "poor",
28
+ "pregnant", "protozoan", "puce", "pulsating", "purple",
29
+ "putrid", "radical", "radioactive", "red", "reformed",
30
+ "reincarnated", "rubber", "Russian", "screaming", "sexy",
31
+ "shiftless", "shifty", "Siamese", "silver", "sin-ridden",
32
+ "sinister", "sizzling", "skeptical", "slack-producing",
33
+ "sleeping", "slick", "slime-dripping", "slimy", "slippery",
34
+ "sluggish", "smoking", "solid gold", "splendid", "squamous",
35
+ "stoned", "sweet", "temporary", "throbbing", "tin-plated",
36
+ "tiny", "transient", "treacherous", "tubular", "ugly",
37
+ "untouchable", "user-friendly", "user-serviceable", "vacant",
38
+ "vacillating", "vampiric", "vibrant", "virginal", "vivid",
39
+ "wealthy", "well-dressed", "white", "wimpy", "worthless",
40
+ "young", "illegal", "tax-free", "drugged", "polluted",
41
+ "flaming", "diseased", "agnostic", "anorexic", "conquering",
42
+ "cosmic", "dancing", "dyslexic", "frenzied", "lumpy", "musical",
43
+ "plump", "perfectly ordinary", "French", "Martian",
44
+ "beautiful", "broken", "corruscating", "enhanced", "frightening",
45
+ "horrific", "mythical", "rugose", "upgraded"]
46
+
47
+ NAMES =
48
+ ["007", "a dead relative", "a long-lost uncle", "a dead rock star",
49
+ "Abdul Al-Azrad", "Abraham Lincoln", "Bill Gates",
50
+ "Albert Einstein", "Ali Baba", "Aladdin",
51
+ "Batman", "Blackrat", "Blackrat Prime", "Buck Rogers",
52
+ "Bullwinkle", "Captain Ahab", "Captain America", "Captain Kirk",
53
+ "Captain Nemo", "Charlie Brown", "Cthulhu", "Dilbert",
54
+ "Darth Vader", "Dave Letterman", "Uncle Bob",
55
+ "Dracula", "Elvis",
56
+ "Erik Bloodaxe", "Evil Stevie", "Fearless Leader",
57
+ "Flaming Carrot",
58
+ "George Lucas", "Gerald Ford",
59
+ "Gandhi", "Grandmother", "Gumby", "Hamlet",
60
+ "Han Solo", "Hitler", "Hulk Hogan",
61
+ "Hunter S. Thompson", "Internal Security",
62
+ "Isaac Asimov", "Jack the Ripper",
63
+ "Jimmy Carter", "Johnny-B-Gud",
64
+ "Joseph Stalin", "King Arthur", "King Tut",
65
+ "Lex Luthor", "Luke Skywalker",
66
+ "Michael Jordan", "Mick Jagger",
67
+ "Mr. Bill", "Mr. Ed", "Mr. Science",
68
+ "Mr. Spock", "Nelson Mandela", "Nixon",
69
+ "Norman Bates", "Obi-Wan Kenobi", "Oliver North",
70
+ "Paul Newman", "Perry Mason", "Princess Leia",
71
+ "Rambo", "Ringo", "Robby the Robot",
72
+ "Robert Heinlein", "Rocky", "Roger Rabbit", "Ronald Reagan",
73
+ "Scrooge", "Sir Lancelot", "Snoopy",
74
+ "Spiderman", "Squad 23", "Steve", "Steven Spielberg", "Superman",
75
+ "the A.C.L.U.", "The Computer", "the Discordian", "Sir Paul",
76
+ "the Hand", "The Illuminati", "the Joker", "the Legion of Doom",
77
+ "the ninja",
78
+ "the Secret Master",
79
+ "the Secret Service", "the vice squad", "Thor",
80
+ "Tiny Tim", "Tweety-Bird", "Uncle Duke", "Weird Al",
81
+ "Winston Churchill", "yo' mama", "your brother",
82
+ "your evil twin", "your father", "your mother", "your sister",
83
+ "Zaphod Beeblebrox", "Zeus", "Zonker", "Donovan's Brain",
84
+ "Jimmy Hoffa", "Tristero", "the Archdean", "Asmodeus",
85
+ "Archangel Gabriel", "Al-Qaeda", "Angel", "Arnold Schwarzenegger",
86
+ "Barry Bonds", "Bill Clinton", "Bill O'Reilly", "Britney Spears",
87
+ "Bud Selig", "Buffy", "Captain Archer", "Condoleeza Rice",
88
+ "Dick Cheney", "Harry Potter", "Harvey Birdman", "Hillary Clinton",
89
+ "Jacques Chirac", "Jay Leno", "Joe Montana", "John Ashcroft",
90
+ "Jon Stewart", "Karl Rove", "LeBron James", "Neo",
91
+ "Osama bin-Laden", "Peter Jackson", "President Bartlett",
92
+ "President Bush", "Saddam Hussein", "Sponge Bob Square Pants",
93
+ "T'Pol", "Tony Blair", "the American Idol", "the Bachelor",
94
+ "the Bachelorette", "the Crocodile Hunter", "the Dixie Chicks",
95
+ "the Powerpuff Girls"
96
+ ]
97
+
98
+ PLACES= ["(not available at your clearance)", "Afghanistan",
99
+ "Alpha Centauri",
100
+ "Alpha Complex", "Atlantis", "Austin", "Baghdad", "Berkeley", "Berlin",
101
+ "Buckingham Palace", "Callahan's Place", "Cheyenne Mountain",
102
+ "Chicago", "Cyberworld", "Dallas", "Death Valley", "Dime Box",
103
+ "Endsville", "Gasoline Alley", "Gotham City", "headquarters", "Hell",
104
+ "Hollywood", "Hong Kong", "Iran", "Iraq", "Israel",
105
+ "Joe's Bar and Grill", "Kabul", "Katmandu", "Lake Geneva", "Las Vegas",
106
+ "left field", "Lithuania", "London", "Los Angeles", "Main Street",
107
+ "Mars", "Middle-earth", "Mission Control", "Mordor", "Moscow",
108
+ "Munich", "my back yard", "Peking", "Poland",
109
+ "San Francisco", "Siberia", "Sixth Street", "SJ Games",
110
+ "Switzerland", "Tel Aviv", "the back forty", "the Bastille",
111
+ "the bathroom", "the best place possible", "the brewery",
112
+ "the Bat Cave", "the corner bar", "the dentists' convention",
113
+ "the doghouse", "the dumpster", "the editorial department",
114
+ "the Empire State Building", "the hackers' convention",
115
+ "the home of a trusted friend", "the Hotel California",
116
+ "the Last National Bank", "the North Pole", "the ocean",
117
+ "the outback", "the Phoenix Project", "the river",
118
+ "the same place as before", "the service station", "the South Pole",
119
+ "the Super Bowl", "the tavern", "the toxic waste dump",
120
+ "the U.S. Attorney's Office", "the Vatican", "the Watergate Hotel",
121
+ "the White House", "Toledo", "Topeka", "Uranus", "Wall Street",
122
+ "you-know-where", "your place", "Yrth", "the Death Star",
123
+ "beautiful downtown Burbank", "Smallville", "the Shire"]
124
+
125
+ PREPOSITIONS=
126
+ ["assumes responsibility for", "avoids servants of",
127
+ "deals with", "elopes with", "evades agents of", "flees from",
128
+ "flees to", "flies to", "flies toward", "goes for", "goes to",
129
+ "has finished in", "has left with", "hides in", "is attacked by",
130
+ "is commanded by", "is concerned about", "is contaminated by",
131
+ "is destroyed by", "is distressed by", "is financed by",
132
+ "is fondled by", "is found by", "is imitated by", "is infiltrated by",
133
+ "is joined by", "is like a god to", "is removed by",
134
+ "is the patron of", "is threatened by", "listens to", "makes fun of",
135
+ "may not visit", "moves to", "originates from", "reports to",
136
+ "retreats from", "returns to", "shoots henchmen of",
137
+ "should watch for", "steals from", "takes blame for",
138
+ "takes control of", "takes no notice of", "takes refuge in",
139
+ "travels to", "walks to", "was eliminated by", "was seen in",
140
+ "will go to", "withdraws from", "assumed responsibility for",
141
+ "avoided servants of", "has dealt with", "eloped with",
142
+ "evaded agents of", "fled from", "fled to", "flew to",
143
+ "flew toward", "has gone for", "went to", "hides in",
144
+ "was attacked by", "was commanded by", "was concerned about",
145
+ "was contaminated by", "was destroyed by", "was distressed by",
146
+ "was financed by", "was fondled by", "was found by",
147
+ "was imitated by", "was infiltrated by", "was joined by",
148
+ "was removed by", "was the patron of", "was threatened by",
149
+ "listened to", "made fun of", "moved to", "originated from",
150
+ "reported to", "retreated from", "returned to", "shot henchmen of",
151
+ "watched for", "stole from", "took blame for", "took control of",
152
+ "took no notice of", "took refuge in", "traveled to",
153
+ "walked to", "withdrew from", "plays with", "played with",
154
+ "is assassinated by", "was assassinated by", "is boggled by",
155
+ "was boggled by", "performs surgical alternations on"]
156
+
157
+ ACTIONS=
158
+ ["amuses", "avoids", "berates", "boggles", "bothers",
159
+ "buries", "catches", "commands", "contaminates", "controls",
160
+ "converts", "delivers", "destroys", "disfigures", "eats", "enters",
161
+ "fondles", "handles", "harasses", "hassles", "helps",
162
+ "imitates", "infiltrates", "inherits", "joins", "kills", "leaves",
163
+ "massages", "molests", "persuades", "perverts", "pitches",
164
+ "rebuilds", "reinforces", "removes", "replaces", "resurrects",
165
+ "saves", "serves", "spanks", "squeezes", "strokes", "subverts",
166
+ "swallows", "swats", "torments", "tortures", "transforms",
167
+ "whips", "teases", "stomps", "mates with", "tickles", "audits",
168
+ "beats", "defeats", "outwits", "manipulates", "defects to",
169
+ "titillates", "perverts", "defenestrates", "discards",
170
+ "abandons", "talks to", "talks back to", "allies with",
171
+ "discovers", "betrays", "assassinates", "promotes",
172
+ "pretends to be", "disguises", "disobeys", "alters",
173
+ "intimidates"]
174
+
175
+ PRONOUNS= ["his", "my", "our", "the", "your"]
176
+
177
+ INTROS=
178
+ ["4 out of 5 dentists recommend that", "Abort immediately unless",
179
+ "Abort previous sequence.", "Advance code sequence.",
180
+ "Alert all stations!", "Confirmed report:",
181
+ "Contrary to popular belief,", "Delete all evidence that",
182
+ "Determine whether", "E.F. Hutton says", "Effective immediately,",
183
+ "Enemy agents now know that", "Fnord!", "Follow plan x if",
184
+ "Ignore previous message.", "Ignore this message.", "Imperative that",
185
+ "It appears that", "It is not true that",
186
+ "Most people surveyed believe that", "Observe and report if",
187
+ "Oral Roberts dreamed that", "Our foes believe that",
188
+ "Our reporters claim that", "Pentagon officials deny that",
189
+ "Please investigate report that", "Step up operation.",
190
+ "Terminate operation if", "The surgeon general warns that",
191
+ "Unsubstantiated rumor:", "Urgent!", "Usual sources confirm that",
192
+ "Warning!", "We suspect that"]
193
+
194
+ NOUNS=
195
+ ["911 file", "(censored)", "amethyst",
196
+ "amulet", "ash tray", "baby", "BBS", "beer bottle", "blueprint",
197
+ "boat", "book", "bowling ball", "business card", "button", "cable",
198
+ "cactus", "cannibal", "capsule", "carnation", "cash", "cat",
199
+ "cauliflower", "chainsaw", "chair", "chicken", "club", "cockroach",
200
+ "code wheel", "coke can", "compact disc", "computer", "cork",
201
+ "couch", "cow", "crystal", "cummerbund",
202
+ "cyberdeck", "demon", "devil", "diamond", "dictaphone", "dictator",
203
+ "dinosaur", "disk drive", "document", "dragon", "drug", "duck",
204
+ "elephant", "engine", "eye", "file", "flag", "floppy disk",
205
+ "fly", "football", "frame", "frog", "geographer", "goldfish",
206
+ "grasshopper", "grimoire", "gyroslugger", "hammer", "hat",
207
+ "hat-rack", "helmet", "hemisphere", "hot tub", "hypodermic",
208
+ "ice cream", "ID card", "iguana", "implement", "infant", "insect",
209
+ "jellybean", "jet", "jet ski", "jukebox", "kitten", "Klingon",
210
+ "krugerrand", "kumquat", "lamp", "light bulb", "machine gun",
211
+ "mallet", "manuscript", "mason jar", "message", "mosquito",
212
+ "motorcycle", "mouse", "oar", "octopus", "olive", "ostrich",
213
+ "paddle", "paintbrush", "paper clip", "passport", "password file",
214
+ "password", "pendant", "penguin", "petunia", "phased plasma rifle",
215
+ "phone", "photocopy", "piranha", "pistol", "pit viper", "plant",
216
+ "playtester", "pop tart", "power drill", "ptarmigan", "pterodactyl",
217
+ "puppy", "pyramid", "racquetball", "radio", "railroad", "razor",
218
+ "rescuer", "ring", "rom chip", "saber", "saxophone", "scenario",
219
+ "scraper", "screwdriver", "scuba mask", "sculpture", "sex toy",
220
+ "shark", "shoggoth", "skateboard", "ski lift", "skillet",
221
+ "spark plug", "spider", "submarine", "surfboard", "sword",
222
+ "teddy bear", "telegram", "television", "tennis ball", "termite",
223
+ "textbook", "toast", "tornado", "traitor", "transmitter",
224
+ "treasure chest", "tree", "trolley", "trumpet", "typewriter",
225
+ "user's manual", "Uzi", "van", "virus", "volleyball", "wand",
226
+ "wheel", "whip", "yak", "Zulu", "INWO deck", "ukelele", "icon",
227
+ "amphibian", "toad", "terminal", "additive", "piccolo", "tuba",
228
+ "angel", "demon", "CD-ROM", "DVD", "Deep One", "Handheld", "MP3",
229
+ "Segway", "dog", "iMac", "laptop", "nanite"]
230
+
231
+ class Fnord
232
+ #Parts of speech functions to return a random word from the arrays and return it if chance==0
233
+ #or if a random integer between 0 and chance-1 equals 0. The (chance==0) check is to optimize
234
+ #by preventing the rand(chance) call when not required. So for a 1 in 7 chance call with chance=7.
235
+ def self.adjective(chance=0)
236
+ (chance==0 or rand(chance)<1) ? ADJECTIVES[rand(ADJECTIVES.length)] : ""
237
+ end
238
+
239
+ def self.name(chance=0)
240
+ (chance==0 or rand(chance)<1) ? NAMES[rand(NAMES.length)] : ""
241
+ end
242
+
243
+ def self.place(chance=0)
244
+ (chance==0 or rand(chance)<1) ? PLACES[rand(PLACES.length)] : ""
245
+ end
246
+
247
+ def self.in_place(chance=0)
248
+ (chance==0 or rand(chance)<1) ? "in #{place}" : ""
249
+ end
250
+
251
+ def self.preposition(chance=0)
252
+ (chance==0 or rand(chance)<1) ? PREPOSITIONS[rand(PREPOSITIONS.length)] : ""
253
+ end
254
+
255
+ def self.action(chance=0)
256
+ (chance==0 or rand(chance)<1) ? ACTIONS[rand(ACTIONS.length)] : ""
257
+ end
258
+
259
+ def self.pronoun(chance=0)
260
+ (chance==0 or rand(chance)<1) ? PRONOUNS[rand(PRONOUNS.length)] : ""
261
+ end
262
+
263
+ def self.intro(chance=0)
264
+ (chance==0 or rand(chance)<1) ? INTROS[rand(INTROS.length)] : ""
265
+ end
266
+
267
+ def self.noun(chance=0)
268
+ (chance==0 or rand(chance)<1) ? NOUNS[rand(NOUNS.length)] : ""
269
+ end
270
+
271
+ def self.headline
272
+ number=rand(14)
273
+ msg=case number #Return a generated Fnord as a string
274
+ when 0 then
275
+ "The #{adjective(2)} #{noun} #{in_place(5)} is #{adjective}."
276
+ when 1 then
277
+ "#{name} #{action} the #{adjective} #{noun} and the #{adjective} #{noun}."
278
+ when 2 then
279
+ "The #{noun} from #{place} will go to #{place}."
280
+ when 3 then
281
+ "#{name} must take the #{adjective} #{noun} from #{place}."
282
+ when 4 then
283
+ "#{place} is #{adjective} and the #{noun} is #{adjective}."
284
+ when 5 then
285
+ "#{name} #{preposition} #{place} for the #{adjective} #{noun}."
286
+ when 6 then
287
+ "The #{adjective(2)} #{noun} #{action} the #{adjective} #{noun} #{in_place(5)}."
288
+ when 7 then
289
+ "#{name} #{preposition} #{place} and #{action} the #{noun}."
290
+ when 8 then
291
+ "#{name} takes #{pronoun} #{adjective(2)} #{noun} and #{preposition} #{place}."
292
+ when 9 then
293
+ "#{name} #{action} the #{adjective(2)} #{noun}."
294
+ when 10 then
295
+ "#{name} #{action} #{name} and #{pronoun} #{adjective(2)} #{noun}."
296
+ when 11 then
297
+ "#{name} is the #{adjective(2)} #{noun}; #{name} #{preposition} #{place}."
298
+ when 12 then
299
+ "You must meet #{name} at #{place} and get the #{adjective(2)} #{noun}."
300
+ when 13 then
301
+ "A #{noun} from #{place} #{action} the #{adjective(2)} #{adjective(5)} #{noun}."
302
+ end
303
+ while msg.include?(" ")
304
+ msg.gsub!(/ /, " ") # Collapse multiple spaces into a single one.
305
+ end
306
+ msg.gsub!(/^ /, "") # remove space from the front
307
+ msg.gsub!(/ \./, ".") # remove space from before a "."
308
+ #Extension to allow for placing #{intro} in front of the messages, and keeping
309
+ #the case correct, if required. Not required for the original set of strings,
310
+ #so commented out to prevent unneccessary code execution.
311
+ #while msg[/([^A-Z][\.\!\?\:])\s+([a-z])/]
312
+ # msg[/([^A-Z][\.\!\?\:])\s+([a-z])/]="#{$1} #{$2.upcase}"
313
+ #end
314
+ msg[0]=msg[0, 1].upcase
315
+ msg
316
+ end
317
+ end
318
+
319
+ match /fnord/
320
+
321
+ def execute(m)
322
+ m.reply Fnord.headline
323
+ end
324
+ end
325
+
326
+ end
327
+
328
+ # AutoLoad
329
+ Bot.config.plugins.plugins.push Plugins::Fnord