├── .ruby-version ├── version.yml ├── .github └── dependabot.yml ├── .gitignore ├── extend ├── array.rb ├── blacklist.rb ├── string.rb ├── opped.rb └── auth.rb ├── bot-control.rb ├── Dockerfile ├── plugins ├── yossarian_plugin.rb ├── ibip.rb ├── genres.rb ├── clickbait.rb ├── leetspeak.rb ├── zalgo │ ├── zalgo.rb │ └── zalgo_text.rb ├── morse_code.rb ├── rot13.rb ├── fortune.rb ├── slap.rb ├── guineapigs.rb ├── borzoi.rb ├── xkcd_comics.rb ├── fieri_quotes │ ├── fieri_quotes.rb │ └── fieri_quotes.txt ├── theo_quotes │ └── theo_quotes.rb ├── flip_text.rb ├── number_facts.rb ├── decisions.rb ├── lennart_quotes │ ├── lennart_quotes.rb │ └── lennart_quotes.txt ├── rms_quotes │ ├── rms_quotes.rb │ └── rms_quotes.txt ├── linus_quotes │ └── linus_quotes.rb ├── zippy_quotes │ └── zippy_quotes.rb ├── rainbow_text.rb ├── bofh_excuses │ └── bofh_excuses.rb ├── command_help.rb ├── new_yorker_cartoons.rb ├── tiny_url.rb ├── taco_recipes.rb ├── dinner.rb ├── regex_replace.rb ├── cstopic.rb ├── beedogs.rb ├── duck_duck_go_search.rb ├── code_eval.rb ├── luther_insults │ └── luther_insults.rb ├── magic8ball.rb ├── wolfram_alpha.rb ├── exchange_rates.rb ├── urban_dictionary.rb ├── wikipedia.rb ├── giphy.rb ├── merriam_webster.rb ├── shakespearean_insults │ ├── shakespearean_insults.rb │ └── shakespearean_insults.yml ├── stock_quotes.rb ├── youtube_search.rb ├── omdb.rb ├── cute_faces.rb ├── ctcp_version.rb ├── ip_info.rb ├── reminders.rb ├── beer_search.rb ├── web_search.rb ├── ping.rb ├── link_titling.rb ├── bot_info.rb ├── myanimelist.rb ├── phone_info.rb ├── last_seen │ └── last_seen.rb ├── weather.rb ├── crypto.rb ├── user_mail │ └── user_mail.rb ├── artist_info.rb ├── user_points │ └── user_points.rb ├── corona.rb ├── custom_triggers │ └── custom_triggers.rb ├── github_info.rb ├── user_intros │ └── user_intros.rb ├── channel_admin.rb ├── channel_moderator │ └── channel_moderator.rb ├── user_quotes │ └── user_quotes.rb ├── now_playing │ └── now_playing.rb ├── catch22.rb └── bot_admin.rb ├── Gemfile ├── .rubocop.yml ├── old_plugins ├── cbsg.rb ├── hastebin.rb ├── cleverbot.rb ├── megahal.rb ├── eth.rb ├── world_population.rb ├── google_translate.rb ├── btc.rb ├── searx.rb ├── ltc.rb ├── isitup.rb ├── weather.rb ├── air_quality.rb ├── google_search.rb ├── jerkcity.rb ├── book_info.rb └── now_reading │ └── now_reading.rb ├── plugins.yml ├── config.yml.example ├── LICENSE ├── WRITING_PLUGINS.md ├── Gemfile.lock ├── yossarian-bot.rb ├── README.md └── .rubocop_todo.yml /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.2 2 | -------------------------------------------------------------------------------- /version.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 2.104.0 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | yossarian-bot.rb.pid 2 | *.yml 3 | !version.yml 4 | !plugins.yml 5 | !plugins/shakespearean_insults/shakespearean_insults.yml 6 | !.rubocop* 7 | *.swp 8 | *~ 9 | .bundle/ 10 | vendor/ 11 | -------------------------------------------------------------------------------- /extend/array.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Array 4 | def exclude?(object) 5 | !include?(object) 6 | end 7 | 8 | def nonempty? 9 | !empty? 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /extend/blacklist.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cinch 4 | module Plugin 5 | module ClassMethods 6 | def use_blacklist 7 | hook :pre, for: [:match, :listen_to], method: :not_blacklisted? 8 | end 9 | end 10 | 11 | def not_blacklisted?(m) 12 | !@bot.blacklist.include?(m.user.nick) && !@bot.blacklist.include?(m.user.host) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bot-control.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # start.rb 5 | # Author: William Woodruff 6 | # ------------------------ 7 | # Starts yossarian-bot.rb using the daemons gem. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "daemons" 13 | 14 | Daemons.run("yossarian-bot.rb") 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.2 2 | 3 | # throw errors if Gemfile has been modified since Gemfile.lock 4 | RUN bundle config --global frozen 1 5 | 6 | WORKDIR /usr/src/app 7 | 8 | COPY Gemfile Gemfile.lock ./ 9 | 10 | # https://github.com/jtoy/cld/issues/10 11 | RUN env CFLAGS=-Wno-narrowing CXXFLAGS=-Wno-narrowing bundle install 12 | 13 | COPY . . 14 | 15 | RUN rm "/usr/src/app/config.yml" 16 | RUN ln -s "/config.yml" "/usr/src/app/config.yml" 17 | 18 | VOLUME ["/config.yml"] 19 | 20 | CMD ["ruby", "yossarian-bot.rb"] 21 | -------------------------------------------------------------------------------- /extend/string.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class String 4 | def normalize_whitespace 5 | self.gsub(/\s+/, " ") 6 | end 7 | 8 | def normalize_whitespace! 9 | self.gsub!(/\s+/, " ") 10 | end 11 | 12 | # http://stackoverflow.com/q/9230663 13 | def unescape_unicode 14 | self.gsub(/\\u(\h{4})/) { |_| [$1].pack("H*").unpack("n*").pack("U*") } 15 | end 16 | 17 | def unescape_unicode! 18 | self.gsub!(/\\u(\h{4})/) { |_| [$1].pack("H*").unpack("n*").pack("U*") } 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /plugins/yossarian_plugin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # yossarian_plugin.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # The superclass that all yossarian-bot plugins extend. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | class YossarianPlugin 12 | # default usage stub 13 | def usage 14 | "" 15 | end 16 | 17 | #default match stub 18 | def match?(_cmd) 19 | false 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "addressable" 6 | gem "daemons" 7 | gem "duck-duck-go" 8 | gem "flippy" 9 | gem "genregen" 10 | gem "grinch", "~> 1.1" 11 | gem "grinch-identify", "~> 1.8" 12 | gem "haste" 13 | gem "htmlentities" 14 | gem "json" 15 | gem "lastfm" 16 | gem "leetspeak" 17 | gem "mechanize" 18 | gem "myanimelist", "~> 1.0" 19 | gem "nokogiri" 20 | gem "open_uri_redirections" 21 | gem "research_topicgen" 22 | gem "rexml" 23 | gem "ruby-eval-in" 24 | gem "ruby-upworthy" 25 | gem "sanitize", "~> 7.0.0" 26 | gem "telegraph" 27 | gem "time_difference" 28 | gem "wolfram", "~> 0.3" 29 | gem "xkcd" 30 | -------------------------------------------------------------------------------- /extend/opped.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cinch 4 | module Plugin 5 | module ClassMethods 6 | def use_opped(silent: false) 7 | if silent 8 | hook :pre, for: [:match], method: :opped_silent? 9 | else 10 | hook :pre, for: [:match], method: :opped? 11 | end 12 | end 13 | end 14 | 15 | def opped_silent?(m) 16 | m.channel && m.channel.opped?(@bot.nick) 17 | end 18 | 19 | def opped?(m) 20 | if opped_silent?(m) 21 | true 22 | else 23 | m.reply "I can\'t administrate this channel.", true 24 | false 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /extend/auth.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cinch 4 | module Plugin 5 | module ClassMethods 6 | def use_auth(silent: false) 7 | if silent 8 | hook :pre, for: [:match], method: :authed_silent? 9 | else 10 | hook :pre, for: [:match], method: :authed? 11 | end 12 | end 13 | end 14 | 15 | def authed_silent?(m) 16 | @bot.admins.include?(m.user.nick) && User(m.user.nick).authed? 17 | end 18 | 19 | def authed?(m) 20 | if authed_silent?(m) 21 | true 22 | else 23 | m.reply "You do not have permission to do that.", true 24 | false 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /plugins/ibip.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ibip.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides IBIP compatibility for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class IBIP < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "[.!:]bots - Announce this bot to the channel." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^([.!:])?bots$/ 23 | end 24 | 25 | match /bots$/, method: :ibip, prefix: /^[.!:]/ 26 | 27 | def ibip(m) 28 | m.reply "Reporting in! [Ruby] See !help." 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | AllCops: 4 | TargetRubyVersion: 2.4 5 | DisplayCopNames: true 6 | DisplayStyleGuide: true 7 | 8 | Style/StringLiterals: 9 | EnforcedStyle: double_quotes 10 | 11 | Style/StringLiteralsInInterpolation: 12 | EnforcedStyle: double_quotes 13 | 14 | Style/TrailingCommaInArrayLiteral: 15 | EnforcedStyleForMultiline: comma 16 | 17 | Style/TrailingCommaInHashLiteral: 18 | EnforcedStyleForMultiline: comma 19 | 20 | Style/FormatString: 21 | EnforcedStyle: percent 22 | 23 | Style/DoubleNegation: 24 | Enabled: false 25 | 26 | Style/RescueModifier: 27 | Enabled: false 28 | 29 | # Disable these temporarily while they're in .rubocop_todo.yml 30 | # Metrics/LineLength: 31 | # Max: 100 32 | # 33 | # Metrics/MethodLength: 34 | # Max: 15 35 | -------------------------------------------------------------------------------- /plugins/genres.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # genres.rb 4 | # Author: Winston Weinert 5 | # ------------------------ 6 | # A Cinch plugin that generates random music genres using 7 | # William Woodruff's genregen. 8 | # ------------------------ 9 | # This code is licensed by Winston Weinert under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "yossarian_plugin" 13 | require "genregen" 14 | 15 | class Genres < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!genre - Generate a random music genre." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?genre$/ 25 | end 26 | 27 | match /genre$/, method: :genre 28 | 29 | def genre(m) 30 | m.reply GenreGen.generate, true 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /plugins/clickbait.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # clickbait.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides clickbait-y titles for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "upworthy" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class Clickbait < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!clickbait - Generate a clickbait-y title." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?clickbait$/ 25 | end 26 | 27 | match /clickbait$/, method: :clickbait 28 | 29 | def clickbait(m) 30 | m.reply Upworthy.headline, true 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /plugins/leetspeak.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bot_admin.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides leetspeak for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "leetspeak" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class LeetSpeak < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!leet - Convert to leetspeak." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?leet$/ 25 | end 26 | 27 | match /leet (.+)/, method: :leetspeak, strip_colors: true 28 | 29 | def leetspeak(m, text) 30 | m.reply text.leet, true 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /plugins/zalgo/zalgo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # zalgo.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that summons Zalgo. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "../yossarian_plugin" 12 | require_relative "zalgo_text" 13 | 14 | class Zalgo < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | def usage 19 | "!zalgo - Summon Zalgo with the given text." 20 | end 21 | 22 | def match?(cmd) 23 | cmd =~ /(!)?zalgo$/ 24 | end 25 | 26 | match /zalgo (.+)/, method: :zalgo, strip_colors: true 27 | 28 | def zalgo(m, text) 29 | m.reply ZalgoText.he_comes(text, up: false, down: false), true 30 | end 31 | end -------------------------------------------------------------------------------- /plugins/morse_code.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # morse_code.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that generates Morse code for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "telegraph" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class MorseCode < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!morse - Convert the given text to Morse code." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?morse$/ 25 | end 26 | 27 | match /morse (.+)/, method: :morse_code, strip_colors: true 28 | 29 | def morse_code(m, text) 30 | m.reply Telegraph.text_to_morse(text), true 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /plugins/rot13.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rot13.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that rot-13 'encryption' for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Rot13 < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "!r13 - 'Encrypt' with the ROT-13 cipher. Alias: !rot13." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^(!)?(r13)|(rot13)$/ 23 | end 24 | 25 | match /r13 (.+)/, method: :rot13 26 | match /rot13 (.+)/, method: :rot13 27 | 28 | def rot13(m, text) 29 | m.reply text.tr("A-Ma-mN-Zn-z", "N-Zn-zA-Ma-m"), true 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /plugins/fortune.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # fortune.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides random Unix fortunes for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Fortune < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "!fortune - Get a Unix fortune." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^(!)?fortune$/ 23 | end 24 | 25 | match /fortune$/, method: :unix_fortune 26 | 27 | def unix_fortune(m) 28 | if system("which fortune 2> /dev/null") 29 | m.reply `fortune`.normalize_whitespace 30 | else 31 | m.reply "Internal error (no fortune)." 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/slap.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # slap.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides user slapping for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Slap < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "!slap - Slap with a large fishbot." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^(!)?slap$/ 23 | end 24 | 25 | match /slap (\S+)/, method: :slap, strip_colors: true 26 | 27 | def slap(m, nick) 28 | if m.channel.has_user?(nick) 29 | m.action_reply "slaps #{nick} with a large fishbot" 30 | else 31 | m.reply "I don\'t see #{nick} in this channel." 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/guineapigs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # guinea.rb 4 | # Author: Bryan Hernandez 5 | # ----------------------- 6 | # Display Guinea Pigs images from an imgur album. 7 | # ----------------------- 8 | # This code is licensed by Bryan Hernandez under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "open-uri" 12 | require "nokogiri" 13 | 14 | require_relative "./yossarian_plugin" 15 | 16 | class GuineaPigs < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | def usage 21 | "!guinea - Get a random guinea pig picture." 22 | end 23 | 24 | def match?(cmd) 25 | cmd =~ /^(!)?guinea$/ 26 | end 27 | 28 | match /guinea$/, method: :guinea 29 | 30 | def guinea(m) 31 | html = Nokogiri::HTML(URI.open("https://imgur.com/r/guineapigs/").read) 32 | img = html.css("a[class=image-list-link]")[rand(1..60)]["href"] 33 | m.reply "https://imgur.com#{img}", true 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /plugins/borzoi.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # borzoi.rb 4 | # Author: William Woodruff 5 | # ----------------------- 6 | # Fetches a random picture of a Borzoi from the Dog CEO API. 7 | # ----------------------- 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "open-uri" 12 | require "json" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class Borzoi < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "https://dog.ceo/api/breed/borzoi/images/random" 21 | 22 | def usage 23 | "!borzoi - Get a random Borzoi picture." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?borzoi$/ 28 | end 29 | 30 | match /borzoi$/, method: :borzoi 31 | 32 | def borzoi(m) 33 | hsh = JSON.parse(URI.open(URL).read) 34 | m.reply hsh["message"], true 35 | rescue Exception => e 36 | m.reply e.to_s, true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugins/xkcd_comics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # xkcd.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides XKCD comics for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "xkcd" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class XKCDComics < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!xkcd [search] - Get a random XKCD comic, or one related to [search]." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?xkcd$/ 25 | end 26 | 27 | match /xkcd$/, method: :xkcd_random 28 | 29 | def xkcd_random(m) 30 | m.reply XKCD.img, true 31 | end 32 | 33 | match /xkcd (.+)/, method: :xkcd_search, strip_colors: true 34 | 35 | def xkcd_search(m, search) 36 | m.reply XKCD.search(search), true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /old_plugins/cbsg.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # cbsg.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that grabs generated corporate bullshit for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "nokogiri" 12 | require "open-uri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class CBSG < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "https://cbsg.sourceforge.io/cgi-bin/live" 21 | 22 | def usage 23 | "!cbsg - Spew some corporate bullshit." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?cbsg$/ 28 | end 29 | 30 | match /cbsg$/, method: :cbsg 31 | 32 | def cbsg(m) 33 | page = Nokogiri::HTML(open(URL).read) 34 | 35 | m.reply page.css("li").first.text, true 36 | rescue Exception => e 37 | m.reply e.to_s, true 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /plugins/fieri_quotes/fieri_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # fieri_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Guy Fieri quotes for yossarian-bot. 7 | # fieri_quotes.txt compiled from various sources. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class FieriQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "fieri_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!fieri - Fetch a random Guy Fieri quote." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?fieri$/ 27 | end 28 | 29 | match /fieri$/, method: :fieri_quote 30 | 31 | def fieri_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/theo_quotes/theo_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # theo_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Theo De Raadt quotes for yossarian-bot. 7 | # theo_quotes.txt generated from Plan9Front's `theo`. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class TheoQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "theo_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!theo - Fetch a random Theo De Raadt quote." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?theo$/ 27 | end 28 | 29 | match /theo$/, method: :theo_quote 30 | 31 | def theo_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /old_plugins/hastebin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # hastebin.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Hastebin interaction to yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "haste" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class Hastebin < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!hb - Post text to Hastebin. Aliases: !haste, !hastebin." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?(hb)|(haste)|(hastebin)$/ 25 | end 26 | 27 | match /hb (.+)/, method: :hastebin 28 | match /haste(?:bin)? (.+)/, method: :hastebin 29 | 30 | def hastebin(m, text) 31 | haste = Haste::Uploader.new 32 | key = haste.upload_raw text 33 | 34 | m.reply "http://hastebin.com/#{key}", true 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /plugins/flip_text.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # flip_text.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that flips text upside down for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "flippy" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class FlipText < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!flip - Flip text upside down or rightside up." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?flip$/ 25 | end 26 | 27 | match /flip down (.+)/, method: :flip_text, strip_colors: true 28 | 29 | def flip_text(m, text) 30 | m.reply text.flip, true 31 | end 32 | 33 | match /flip up (.+)/, method: :unflip_text, strip_colors: true 34 | 35 | def unflip_text(m, text) 36 | m.reply text.unflip, true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugins/number_facts.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # number_facts.rb 4 | # Author: William Woodruff 5 | # ----------------------- 6 | # Fetches a random fact about a given number from the Numbers API. 7 | # ----------------------- 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "open-uri" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class NumberFacts < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | URL = "http://numbersapi.com/%d/math" 20 | 21 | def usage 22 | "!number - Get a random fact about a number." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?number$/ 27 | end 28 | 29 | match /number (\d+)/, method: :number_fact 30 | 31 | def number_fact(m, number) 32 | url = URL % { number: number } 33 | fact = URI.open(url).read 34 | m.reply fact, true 35 | rescue Exception => e 36 | m.reply e.to_s, true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /old_plugins/cleverbot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # cleverbot.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Cleverbot interaction to yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "cleverbot-api" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class Cleverbot < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def initialize(*args) 20 | super 21 | @cb = CleverBot.new 22 | end 23 | 24 | def usage 25 | "!cb - Talk to CleverBot. Alias: !cleverbot." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?(cb$)|(cleverbot$)/ 30 | end 31 | 32 | match /cb (.+)/, method: :cleverbot, strip_colors: true 33 | match /cleverbot (.+)/, method: :cleverbot, strip_colors: true 34 | 35 | def cleverbot(m, msg) 36 | m.reply @cb.think(msg), true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /plugins/decisions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # decisions.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides decisions for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Decisions < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "!decide - Decide between choices. Delimiters are 'OR', 'or', and '||'." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^(!)?decide$/ 23 | end 24 | 25 | match /decide (.+)/, method: :decide, strip_colors: true 26 | 27 | def decide(m, query) 28 | choices = query.split(/ (?:OR|or|\|\|) |,/).map(&:strip).map(&:downcase).uniq 29 | if choices.size < 2 30 | m.reply %w[Yep. Nope.].sample, true 31 | else 32 | m.reply choices.sample, true 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /plugins/lennart_quotes/lennart_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # lennart_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Lennart Poettering quotes for yossarian-bot. 7 | # lennart_quotes.txt graciously collected by "rewt". 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class LennartQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "lennart_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!lennart - Fetch a random Lennart Poettering quote." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?lennart$/ 27 | end 28 | 29 | match /lennart$/, method: :lennart_quote 30 | 31 | def lennart_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/rms_quotes/rms_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rms_quotes.rb 4 | # Author: Winston Weinert 5 | # ------------------------ 6 | # A Cinch plugin that provides Richard Stallman (rms) quotes for yossarian-bot. 7 | # rms_quotes.txt is parsed from https://github.com/faiq/rms 8 | # ------------------------ 9 | # This code is licensed by Winston Weinert under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class RMSQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "rms_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!rms - Fetch a random Richard Stallman quote. Aliases: !stallman." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?(rms|stallman)$/ 27 | end 28 | 29 | match /(rms|stallman)$/, method: :rms_quote 30 | 31 | def rms_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/linus_quotes/linus_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # linus_quotes.rb 4 | # Author: Winston Weinert 5 | # ------------------------ 6 | # A Cinch plugin that provides Linus Torvalds quotes for yossarian-bot. 7 | # linus_quotes.txt generated from Wikiquote's article 8 | # ------------------------ 9 | # This code is licensed by Winston Weinert under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class LinusQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "linus_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!linus - Fetch a random Linus Torvalds quote. Aliases: !torvalds." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?(linus|torvalds)$/ 27 | end 28 | 29 | match /(linus|torvalds)$/, method: :linus_quote 30 | 31 | def linus_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /plugins/zippy_quotes/zippy_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # zippy_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Zippy the Pinhead quotes for yossarian-bot. 7 | # zippy_quotes.txt is converted from the original (?) MIT "yow.lines". 8 | # ------------------------ 9 | # This code is licensed by Winston Weinert under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "../yossarian_plugin" 13 | 14 | class ZippyQuotes < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | QUOTES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "zippy_quotes.txt")) 19 | QUOTES = File.readlines(QUOTES_FILE) 20 | 21 | def usage 22 | "!zippy - Fetch a random Zippy the Pinhead quote. Aliases: !pinhead." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?(zippy|pinhead)$/ 27 | end 28 | 29 | match /(zippy|pinhead)$/, method: :zippy_quote 30 | 31 | def zippy_quote(m) 32 | m.reply QUOTES.sample 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /plugins/rainbow_text.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rainbow_text.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that generates rainbow text for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class RainbowText < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | COLORS = ["03", "04", "06", "07", "08", "09", "11", "13"] 18 | 19 | def usage 20 | "!rainbow - Vomit out rainbowified text. Alias: !vomit." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?(rainbow)|(vomit)$/ 25 | end 26 | 27 | match /rainbow (.+)/, method: :rainbow_text, strip_colors: true 28 | match /vomit (.+)/, method: :rainbow_text, strip_colors: true 29 | 30 | def rainbow_text(m, text) 31 | color_text = text.chars.map do |c| 32 | "\x03#{COLORS.sample}#{c}\x0F" 33 | end.join("") 34 | 35 | m.reply color_text, true 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /plugins/bofh_excuses/bofh_excuses.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bofh_excuses.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides BOFH excuses for yossarian-bot. 7 | # bofh_excuses.txt adapted from Jeff Ballard's BOFH Excuse Server: 8 | # http://pages.cs.wisc.edu/~ballard/bofh/ 9 | # ------------------------ 10 | # This code is licensed by William Woodruff under the MIT License. 11 | # http://opensource.org/licenses/MIT 12 | 13 | require_relative "../yossarian_plugin" 14 | 15 | class BOFHExcuses < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | EXCUSES_FILE = File.expand_path(File.join(File.dirname(__FILE__), "bofh_excuses.txt")) 20 | EXCUSES = File.readlines(EXCUSES_FILE) 21 | 22 | def usage 23 | "!bofh - Fetch a random Bastard Operator From Hell excuse." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?bofh$/ 28 | end 29 | 30 | match /bofh/, method: :bofh, strip_colors: true 31 | 32 | def bofh(m) 33 | excuse = EXCUSES.sample 34 | 35 | m.reply excuse 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /plugins/command_help.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # command_help.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides command help for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class CommandHelp < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def usage 18 | "!help [cmd] - Display general help, or help for [cmd]." 19 | end 20 | 21 | def match?(cmd) 22 | cmd =~ /^(!)?help$/ 23 | end 24 | 25 | match /help$/, method: :help 26 | 27 | def help(m) 28 | m.reply "Commands: http://git.io/38F1qA - Use !help for info.", true 29 | end 30 | 31 | match /help (\S+)/, method: :help_cmd 32 | 33 | def help_cmd(m, cmd) 34 | @bot.plugins.each do |plugin| 35 | if plugin.respond_to?(:match?) && plugin.match?(cmd) 36 | m.reply plugin.usage, true 37 | return 38 | end 39 | end 40 | m.reply "Nothing found for \'#{cmd}\'.", true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /old_plugins/megahal.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bot_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides interaction with MegaHAL for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "megahal" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class HAL < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def initialize(*args) 20 | super 21 | @hal = MegaHAL.new 22 | end 23 | 24 | def usage 25 | "!hal9000 - Talk to MegaHAL. Alias: !hal9k." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?(hal9000$)|(hal9k$)/ 30 | end 31 | 32 | listen_to :channel 33 | 34 | def listen(m) 35 | if m.message !~ /^[!:.]/ 36 | @hal.reply(m.message) # train hal on channel messages 37 | end 38 | end 39 | 40 | match /hal9000 (.+)/, method: :hal, strip_colors: true 41 | match /hal9k (.+)/, method: :hal, strip_colors: true 42 | 43 | def hal(m, msg) 44 | m.reply @hal.reply(msg), true 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /plugins/new_yorker_cartoons.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # xkcd.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides New Yorker cartoons for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "htmlentities" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class NewYorkerCartoons < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | URL = "https://www.newyorker.com/cartoons/random/randomAPI" 20 | 21 | def usage 22 | "!ny - Get a random New Yorker cartoon." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?ny$/ 27 | end 28 | 29 | match /ny$/, method: :random_cartoon 30 | 31 | def random_cartoon(m) 32 | comic = JSON.parse(URI.open(URL).read).first 33 | caption = HTMLEntities.new.decode comic["caption"] 34 | 35 | if caption.empty? 36 | m.reply comic["src"], true 37 | else 38 | m.reply "#{caption} - #{comic["src"]}", true 39 | end 40 | rescue Exception => e 41 | m.reply e.to_s, true 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /plugins/tiny_url.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # tiny_url.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides TinyURL interaction to yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "open-uri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class TinyURL < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "http://tinyurl.com/api-create.php?url=%{link}" 21 | 22 | def usage 23 | "!turl - Shorten the given using TinyURL. Alias: !tinyurl." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?t(?:iny)?url$/ 28 | end 29 | 30 | match /t(?:iny)?url (#{URI.regexp(['http', 'https'])})/, method: :tinyurl, strip_colors: true 31 | 32 | def tinyurl(m, link) 33 | url = URL % { link: Addressable::URI.encode(link) } 34 | 35 | begin 36 | short_link = URI.open(url).read 37 | m.reply short_link, true 38 | rescue Exception => e 39 | m.reply e.to_s, true 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /plugins/taco_recipes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # taco_recipes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides random taco recipes to yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "json" 12 | require "open-uri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class TacoRecipes < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "http://taco-randomizer.herokuapp.com/random/?full-taco=true" 21 | 22 | def usage 23 | "!taco - Get a random taco recipe from the Taco Randomizer." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?taco$/ 28 | end 29 | 30 | match /taco$/, method: :random_taco 31 | 32 | def random_taco(m) 33 | hash = JSON.parse(URI.open(URL).read) 34 | recipe_url = hash["url"].gsub(/(raw\.github.com)|(\/master\/)/, "raw.github.com" => "github.com", "/master/" => "/blob/master/") 35 | 36 | m.reply "#{hash['name']} - #{recipe_url}", true 37 | rescue Exception => e 38 | m.reply e.to_s, true 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /plugins/dinner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # dinner.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves random dinner recipes for yossarian-bot. 7 | # Recipes curated by Zach Golden: http://whatthefuckshouldimakefordinner.com 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "nokogiri" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class Dinner < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "http://whatthefuckshouldimakefordinner.com" 22 | 23 | def usage 24 | "!dinner - Retrieve a random dinner recipe." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?dinner$/ 29 | end 30 | 31 | match /dinner$/, method: :dinner 32 | 33 | def dinner(m) 34 | page = Nokogiri::HTML(URI.open(URL).read) 35 | 36 | food = page.css("dl").map(&:text).join.strip.tr("\n", " ") 37 | link = page.css("dt")[1].css("a").first["href"] 38 | 39 | m.reply "#{food}. #{link}", true 40 | rescue Exception => e 41 | m.reply e.to_s, true 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /plugins/regex_replace.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # regex_replace.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that keeps track of a user's last message, 7 | # and allows them to apply a regex to it. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "yossarian_plugin" 13 | 14 | class RegexReplace < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | def initialize(*args) 19 | super 20 | @users = {} 21 | end 22 | 23 | listen_to :channel 24 | 25 | def listen(m) 26 | if m.message !~ /^s\/([^\/]*)\/([^\/]*)(\/)?$/ 27 | @users[m.user.nick] = m.message 28 | end 29 | end 30 | 31 | match /^s\/([^\/]*)\/([^\/]*)(\/)?$/, use_prefix: false, method: :sed 32 | 33 | def sed(m, orig, repl) 34 | if @users.key?(m.user.nick) 35 | mod = @users[m.user.nick].sub(Regexp.new(orig), repl) 36 | m.reply "#{m.user.nick} probably meant: #{mod}" 37 | @users.delete(m.user.nick) 38 | else 39 | m.reply "No previous message to operate on.", true 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /plugins/cstopic.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # cstopic.rb 4 | # Author: slackR 5 | # ------------------------ 6 | # A Cinch plugin that generates CS research topics for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by slackR under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "research_topicgen" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class CSTopics < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!topic - Generate worthy research topics." 21 | end 22 | 23 | match /topic (.+)/, method: :topic 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?topic$/ 27 | end 28 | 29 | def topic(m, search) 30 | if search =~ /cs/ 31 | m.reply ResearchTopicGen.cs, true 32 | elsif search =~ /system/ 33 | m.reply ResearchTopicGen.system, true 34 | elsif search =~ /crypto/ 35 | m.reply ResearchTopicGen.crypto, true 36 | elsif search =~ /random/ 37 | m.reply ResearchTopicGen.send([:cs, :system, :crypto].sample), true 38 | else 39 | m.reply "Research Topics for #{topic} can't be generated.", true 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /old_plugins/eth.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # eth.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves Ethereum exchange rates for yossarian-bot. 7 | # Data courtesy of https://coinmarketcap-nexuist.rhcloud.com/api/eth 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "open-uri" 13 | require "json" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class ETH < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://coinmarketcap-nexuist.rhcloud.com/api/eth" 22 | 23 | def usage 24 | "!eth - Get the current Ethereum exchange rate in USD." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?eth$/ 29 | end 30 | 31 | match /eth$/, method: :eth_rate 32 | 33 | def eth_rate(m) 34 | hash = JSON.parse(URI.open(URL).read) 35 | rate = hash["price"]["usd"].round(2) 36 | change = hash["change"].to_f 37 | 38 | direction = (change > 0 ? "↑" : "↓") 39 | m.reply "1 ETH = #{rate} USD | #{direction}", true 40 | 41 | rescue Exception => e 42 | m.reply e.to_s, true 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /plugins/beedogs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # beedogs.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves pictures of Beedogs for yossarian-bot. 7 | # Beedogs curated by Gina Zycher: http://beedogs.com 8 | # Now that http://beedogs.com is dead, http://beedog.github.io is used instead. 9 | # ------------------------ 10 | # This code is licensed by William Woodruff under the MIT License. 11 | # http://opensource.org/licenses/MIT 12 | 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class Beedogs < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "http://beedog.github.io" 22 | COUNT_URL = "#{URL}/count" 23 | IMAGE_URL = "#{URL}/image%{num}.png" 24 | 25 | def initialize(*args) 26 | super 27 | @count = URI.open(COUNT_URL).read.to_i 28 | end 29 | 30 | def usage 31 | "!beedog - Retrieve a random picture of a beedog." 32 | end 33 | 34 | def match?(cmd) 35 | cmd =~ /^(!)?beedog$/ 36 | end 37 | 38 | match /beedog$/, method: :beedog 39 | 40 | def beedog(m) 41 | num = rand(1..@count) 42 | url = IMAGE_URL % { num: num } 43 | 44 | m.reply url, true 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /plugins/duck_duck_go_search.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # duck_duck_go.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides DuckDuckGo interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "duck_duck_go" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class DuckDuckGoSearch < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def initialize(*args) 20 | super 21 | @ddg = DuckDuckGo.new 22 | end 23 | 24 | def usage 25 | "!ddg - Search DuckDuckGo's Zero Click Info API." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?ddg$/ 30 | end 31 | 32 | match /ddg (.+)/, method: :ddg_search, strip_colors: true 33 | 34 | def ddg_search(m, search) 35 | begin 36 | zci = @ddg.zeroclickinfo(search) 37 | response = zci.abstract_text || "No results for '#{search}'." 38 | rescue JSON::ParserError => e 39 | # known bug in gem: https://github.com/andrewrjones/ruby-duck-duck-go/issues/6 40 | response = "No results for '#{search}'." 41 | end 42 | 43 | m.reply response, true 44 | end 45 | end 46 | 47 | -------------------------------------------------------------------------------- /old_plugins/world_population.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # btc.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves world population estimates for yossarian-bot. 7 | # Data courtesy of the US census: www.census.gov/popclock/data/population/world 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "open-uri" 13 | require "json" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class WorldPopulation < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://www.census.gov/popclock/data/population/world" 22 | 23 | def usage 24 | "!wp - Get the approximate world population and growth rate. Alias: !population." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?(wp$)|(population$)/ 29 | end 30 | 31 | match /(wp$)|(population$)/, method: :world_population 32 | 33 | def world_population(m) 34 | hash = JSON.parse(URI.open(URL).read) 35 | pop = hash["world"]["population"] 36 | rate = hash["world"]["population_rate"] 37 | m.reply "World population: #{pop} (#{rate}/second)", true 38 | rescue Exception => e 39 | m.reply e.to_s, true 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /old_plugins/google_translate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # google_translate.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin for interfacing with Google Translate. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class GoogleTranslate < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://translate.googleapis.com/translate_a/t?client=a&sl=auto&tl=en&q=%{query}" 22 | 23 | def usage 24 | "!tr - Translate to English. Alias: !translate." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?tr(?:anslate)?$/ 29 | end 30 | 31 | match /tr(?:anslate)? (.+)/, method: :google_translate_auto, strip_colors: true 32 | 33 | def google_translate_auto(m, msg) 34 | query = Addressable::URI.encode(msg) 35 | url = URL % { query: query } 36 | 37 | begin 38 | hash = JSON.parse(URI.open(url).read) 39 | result = hash["sentences"].first["trans"] 40 | m.reply result, true 41 | rescue Exception => e 42 | m.reply e.to_s, true 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /old_plugins/btc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # btc.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves Bitcoin exchange rates for yossarian-bot. 7 | # Data courtesy of the BitcoinAverage Price Index: https://bitcoinaverage.com/ 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "open-uri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class BTC < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "https://api.bitcoinaverage.com/ticker/global/USD/last" 21 | 22 | def initialize(*args) 23 | super 24 | @last_trade = nil 25 | end 26 | 27 | def usage 28 | "!btc - Get the current Bitcoin exchange rate in USD." 29 | end 30 | 31 | def match?(cmd) 32 | cmd =~ /^(!)?btc$/ 33 | end 34 | 35 | match /btc$/, method: :btc_rate 36 | 37 | def btc_rate(m) 38 | rate = URI.open(URL).read 39 | 40 | if @last_trade.nil? 41 | m.reply "1 BTC = #{rate} USD", true 42 | else 43 | direction = (@last_trade < rate ? "↑" : "↓") 44 | m.reply "1 BTC = #{rate} USD | #{direction}", true 45 | end 46 | 47 | @last_trade = rate 48 | 49 | rescue Exception => e 50 | m.reply e.to_s, true 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /old_plugins/searx.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # searx.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Searx interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class Searx < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://searx.me/?q=%{query}&format=json" 22 | 23 | def usage 24 | "!s - Search Searx." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?s$/ 29 | end 30 | 31 | match /s (.+)/, method: :searx_search, strip_colors: true 32 | 33 | def searx_search(m, search) 34 | query = Addressable::URI.encode(search) 35 | url = URL % { query: query } 36 | 37 | begin 38 | hash = JSON.parse(open(url).read) 39 | result = hash["results"].first 40 | 41 | if result 42 | site = result["url"] 43 | content = result["content"] 44 | 45 | m.reply "#{site} - #{content}" 46 | else 47 | m.reply "No Searx results for '#{search}'.", true 48 | end 49 | rescue Exception => e 50 | m.reply e.to_s, true 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /old_plugins/ltc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ltc.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves Litecoin exchange rates for yossarian-bot. 7 | # Data courtesy of the BTC-e exchange: https://btc-e.com 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "open-uri" 13 | require "json" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class LTC < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://btc-e.com/api/3/ticker/ltc_usd" 22 | 23 | def initialize(*args) 24 | super 25 | @last_trade = nil 26 | end 27 | 28 | def usage 29 | "!ltc - Get the current Litecoin exchange rate in USD." 30 | end 31 | 32 | def match?(cmd) 33 | cmd =~ /^(!)?ltc$/ 34 | end 35 | 36 | match /ltc$/, method: :ltc_rate 37 | 38 | def ltc_rate(m) 39 | hash = JSON.parse(URI.open(URL).read) 40 | rate = hash["ltc_usd"]["buy"].round(2) 41 | 42 | if @last_trade.nil? 43 | m.reply "1 LTC = #{rate} USD", true 44 | else 45 | direction = (@last_trade < rate ? "↑" : "↓") 46 | m.reply "1 LTC = #{rate} USD | #{direction}", true 47 | end 48 | 49 | @last_trade = rate 50 | 51 | rescue Exception => e 52 | m.reply e.to_s, true 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /plugins/code_eval.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # code_eval.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides code evaluation for yossarian-bot. 7 | # Uses the eval.in service: https://eval.in/ 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "eval-in" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class CodeEval < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | def usage 21 | "!eval - Evaluate the given code with the given language on eval.in. (Use `!eval' to get a list of languages.)" 22 | end 23 | 24 | def match?(cmd) 25 | cmd =~ /^(!)?eval$/ 26 | end 27 | 28 | match /eval (\S+) (.+)/, method: :code_eval, strip_colors: true 29 | 30 | def code_eval(m, lang, code) 31 | result = EvalIn.eval(lang, code) 32 | 33 | m.reply result.output.normalize_whitespace, true 34 | rescue EvalIn::BadLanguageError 35 | m.reply "I don\'t know #{lang}.", true 36 | rescue EvalIn::ConnectionError 37 | m.reply "Failure while connecting to evaluation service.", true 38 | end 39 | 40 | match /eval$/, method: :list_languages, strip_colors: true 41 | 42 | def list_languages(m) 43 | langs = EvalIn::Result::LANGS.keys * ", " 44 | m.reply "Known languages: #{langs}", true 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /plugins/luther_insults/luther_insults.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # luther_insults.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that grabs Insults by Martin Luther for yossarian-bot. 7 | # luther_insults.txt from http://ergofabulous.org/luther/insult-list.php 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "nokogiri" 13 | 14 | require_relative "../yossarian_plugin" 15 | 16 | class LutherInsults < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | INSULTS_FILE = File.expand_path(File.join(File.dirname(__FILE__), "luther_insults.txt")) 21 | INSULTS = File.readlines(INSULTS_FILE) 22 | 23 | def usage 24 | "!luther [nick] - Fetch a random insult from Martin Luther's oeuvre and direct it at a nickname if given." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?luther$/ 29 | end 30 | 31 | match /luther$/, method: :luther_insult 32 | 33 | def luther_insult(m) 34 | m.reply INSULTS.sample, true 35 | end 36 | 37 | match /luther (\S+)/, method: :luther_insult_nick, strip_colors: true 38 | 39 | def luther_insult_nick(m, nick) 40 | if m.channel.has_user?(nick) 41 | insult = INSULTS.sample 42 | m.reply "#{nick}: #{insult}" 43 | else 44 | m.reply "I don\'t see #{nick} in this channel." 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /plugins/magic8ball.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # magic8ball.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Magic 8 Ball answers for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Magic8Ball < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | ANSWERS = [ 18 | "It is certain.", 19 | "It is decidedly so.", 20 | "Without a doubt.", 21 | "Yes definitely.", 22 | "You may rely on it.", 23 | "As I see it, yes.", 24 | "Most likely.", 25 | "Outlook good.", 26 | "Yes.", 27 | "Signs point to yes.", 28 | "Reply hazy, try again.", 29 | "Ask again later.", 30 | "Better not tell you now.", 31 | "Cannot predict now.", 32 | "Concentrate and ask again.", 33 | "Don't count on it.", 34 | "My reply is no.", 35 | "My sources say no.", 36 | "Outlook not so good.", 37 | "Very doubtful.", 38 | ] 39 | 40 | def usage 41 | "!8ball - Ask the Magic 8 Ball a question. Alias: !8b." 42 | end 43 | 44 | def match?(cmd) 45 | cmd =~ /^(!)?(8ball)|(8b)$/ 46 | end 47 | 48 | match /8b (.+)/, method: :magic8ball 49 | match /8ball (.+)/, method: :magic8ball 50 | 51 | def magic8ball(m) 52 | m.reply ANSWERS.sample, true 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /plugins.yml: -------------------------------------------------------------------------------- 1 | --- 2 | plugins: 3 | - ArtistInfo 4 | - Beedogs 5 | - BeerSearch 6 | - BOFHExcuses 7 | - Borzoi 8 | - BotInfo 9 | - BotAdmin 10 | - Catch22 11 | - ChannelAdmin 12 | - ChannelModerator 13 | - Clickbait 14 | - CommandHelp 15 | - CodeEval 16 | - Corona 17 | - Crypto 18 | - CSTopics 19 | - CTCPVersion 20 | - CustomTriggers 21 | - CuteFaces 22 | - Decisions 23 | - Dinner 24 | - DuckDuckGoSearch 25 | - ExchangeRates 26 | - FieriQuotes 27 | - FlipText 28 | - Fortune 29 | - Genres 30 | - GitHubInfo 31 | - Giphy 32 | - GuineaPigs 33 | - IBIP 34 | - IPInfo 35 | - LastSeen 36 | - LeetSpeak 37 | - LennartQuotes 38 | - LinkTitling 39 | - LinusQuotes 40 | - LutherInsults 41 | - Magic8Ball 42 | - MerriamWebster 43 | - MorseCode 44 | - MyAnimeListSearch 45 | - NewYorkerCartoons 46 | - NowPlaying 47 | - NumberFacts 48 | - OMDB 49 | - Weather 50 | - PhoneInfo 51 | - Ping 52 | - RainbowText 53 | - RegexReplace 54 | - Reminders 55 | - RMSQuotes 56 | - Rot13 57 | - ShakespeareanInsults 58 | - Slap 59 | - StockQuotes 60 | - TacoRecipes 61 | - TheoQuotes 62 | - TinyURL 63 | - UserIntros 64 | - UserQuotes 65 | - UserMail 66 | - UserPoints 67 | - UrbanDictionary 68 | - WebSearch 69 | - Wikipedia 70 | - WolframAlpha 71 | - XKCDComics 72 | - YouTubeSearch 73 | - Zalgo 74 | - ZippyQuotes 75 | -------------------------------------------------------------------------------- /plugins/wolfram_alpha.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # wolfram_alpha.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Wolfram|Alpha interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "wolfram" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class WolframAlpha < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | KEY = ENV["WOLFRAM_ALPHA_APPID_KEY"] 20 | 21 | def usage 22 | "!wa - Ask Wolfram|Alpha about . Alias: !wolfram." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?(wolfram)|(wa)$/ 27 | end 28 | 29 | match /wa (.+)/, method: :wolfram_alpha, strip_colors: true 30 | match /wolfram (.+)/, method: :wolfram_alpha, strip_colors: true 31 | 32 | def wolfram_alpha(m, query) 33 | if KEY 34 | Wolfram.appid = KEY 35 | result = Wolfram.fetch(query).pods[1] 36 | 37 | if result && !result.plaintext.empty? 38 | # wolfram alpha formats unicode as \:xxxx for some unknown reason 39 | text = result.plaintext.gsub("\\:", "\\u") 40 | 41 | text.unescape_unicode! 42 | text.normalize_whitespace! 43 | 44 | m.reply text, true 45 | else 46 | m.reply "Wolfram|Alpha has nothing for #{query}", true 47 | end 48 | else 49 | m.reply "#{self.class.name}: Internal error (missing API key)." 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /old_plugins/isitup.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # isitup.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides IsItUp.org functionality for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class IsItUp < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://isitup.org/%{domain}.json" 22 | 23 | def usage 24 | "!isitup - Check whether or not is currently online. Alias: !up." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?(?:isit)?up$/ 29 | end 30 | 31 | match /(?:isit)?up (.+)/, method: :isitup, strip_colors: true 32 | 33 | def isitup(m, site) 34 | domain = Addressable::URI.encode_component(site.gsub(/^http(?:s)?:\/\//, "")) 35 | url = URL % { domain: domain } 36 | 37 | begin 38 | hash = JSON.parse(URI.open(url).read) 39 | 40 | response_code = hash["response_code"] 41 | 42 | case hash["status_code"] 43 | when 1 44 | m.reply "#{domain} is currently online [#{response_code}].", true 45 | when 2 46 | m.reply "#{domain} is currently offline.", true 47 | when 3 48 | m.reply "#{domain} is not a valid domain.", true 49 | end 50 | rescue Exception => e 51 | m.reply e.to_s, true 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /old_plugins/weather.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # weather.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Weather Underground interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "wunderground" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class Weather < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | KEY = ENV["WUNDERGROUND_API_KEY"] 20 | 21 | def usage 22 | "!w - Get the weather at . Alias: !weather." 23 | end 24 | 25 | def match?(cmd) 26 | cmd =~ /^(!)?(weather)|(^w)$/ 27 | end 28 | 29 | match /w (.+)/, method: :weather, strip_colors: true 30 | match /weather (.+)/, method: :weather, strip_colors: true 31 | 32 | def weather(m, location) 33 | if KEY 34 | wu = Wunderground.new(KEY) 35 | hash = wu.conditions_for(location) 36 | 37 | if hash["current_observation"] 38 | loc = hash["current_observation"]["display_location"]["full"] 39 | weather = hash["current_observation"]["weather"] 40 | temp = hash["current_observation"]["temperature_string"] 41 | m.reply "Current temperature in #{loc} is #{temp} and #{weather}.", true 42 | else 43 | m.reply "Bad query for location \'#{location}\'.", true 44 | end 45 | else 46 | m.reply "#{self.class.name}: Internal error (missing API key)." 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /plugins/exchange_rates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # exchange_rates.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides USD currency exchange rates for yossarian-bot. 7 | # Data courtesy of Open Exchange Rates: https://openexchangerates.org/ 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class ExchangeRates < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["OEX_API_KEY"] 22 | URL = "https://openexchangerates.org/api/latest.json?app_id=%{key}" 23 | 24 | def usage 25 | "!rate - Get the currency exchange rate between USD and one or more currencies." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?rate$/ 30 | end 31 | 32 | match /rate (.+)/, method: :exchange_rate, strip_colors: true 33 | 34 | def exchange_rate(m, code) 35 | if KEY 36 | url = URL % { key: KEY } 37 | codes = code.upcase.split 38 | 39 | begin 40 | hash = JSON.parse(URI.open(url).read) 41 | hash["rates"].default = "?" 42 | 43 | rates = codes.map do |curr| 44 | "USD/#{curr}: #{hash['rates'][curr]}" 45 | end.join(", ") 46 | 47 | m.reply rates, true 48 | rescue Exception => e 49 | m.reply e.to_s, true 50 | end 51 | else 52 | m.reply "#{self.class.name}: Internal error (missing API key)." 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /plugins/urban_dictionary.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # urban_dictionary.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides UrbanDictionary interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class UrbanDictionary < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "http://api.urbandictionary.com/v0/define?term=%{query}" 22 | 23 | def usage 24 | "!ud - Look up on UrbanDictionary. Alias: !urban." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?(ud)|(urban)$/ 29 | end 30 | 31 | match /ud (.+)/, method: :urban_dict, strip_colors: true 32 | match /urban (.+)/, method: :urban_dict, strip_colors: true 33 | 34 | def urban_dict(m, phrase) 35 | query = Addressable::URI.encode_component(phrase) 36 | url = URL % { query: query } 37 | 38 | begin 39 | hash = JSON.parse(URI.open(url).read) 40 | 41 | if hash["list"].nonempty? 42 | list = hash["list"].first 43 | definition = list["definition"][0..255].normalize_whitespace 44 | link = list["permalink"] 45 | m.reply "#{phrase} - #{definition}... (#{link})", true 46 | else 47 | m.reply "UrbanDictionary has nothing for #{phrase}." 48 | end 49 | rescue Exception => e 50 | m.reply e.to_s, true 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /plugins/wikipedia.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # wikipedia.rb 4 | # Authors: slackR (slackErEhth77) and William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin to get info from Wikipedia for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by slackErEhth77 under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class Wikipedia < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://en.wikipedia.org/w/api.php?action=opensearch&format=json&redirects=resolve&search=%{query}" 22 | 23 | def usage 24 | "!wiki - Search Wikipedia for the given ." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?wiki$/ 29 | end 30 | 31 | match /wiki (.+)/, method: :search_wiki, strip_colors: true 32 | 33 | def search_wiki(m, search) 34 | query = Addressable::URI.encode_component(search) 35 | url = URL % { query: query } 36 | 37 | begin 38 | results = JSON.parse(URI.open(url).read) 39 | if results[1].nonempty? 40 | content = if results[2].first.empty? 41 | "No extract provided." 42 | else 43 | results[2].first 44 | end 45 | link = results[3].first.sub("https://en.wikipedia.org/wiki/", "http://enwp.org/") 46 | 47 | m.reply "#{link} - #{content}", true 48 | else 49 | m.reply "No results for #{search}.", true 50 | end 51 | rescue Exception => e 52 | m.reply e.to_s, true 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /plugins/giphy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # giphy.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves GIFs from Giphy for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class Giphy < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["GIPHY_API_KEY"] 22 | URL = "http://api.giphy.com/v1/gifs/search?q=%{query}&api_key=%{key}&limit=100" 23 | 24 | def usage 25 | "!giphy - Search Giphy for GIFs. Alias: !gif." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?gi(?:phy|f)$/ 30 | end 31 | 32 | match /gi(?:phy|f) (.+)$/, method: :giphy, strip_colors: true 33 | 34 | def giphy(m, search) 35 | if KEY 36 | query = Addressable::URI.encode_component(search) 37 | url = URL % { query: query, key: KEY } 38 | 39 | begin 40 | hash = JSON.parse(URI.open(url).read) 41 | 42 | if hash["data"].nonempty? 43 | gif = hash["data"].sample["images"]["original"] 44 | 45 | gif_url = gif["url"] 46 | gif_dim = "#{gif['width']}x#{gif['height']}" 47 | 48 | m.reply "#{gif_url} (#{gif_dim})", true 49 | else 50 | m.reply "No Giphy results for #{search}.", true 51 | end 52 | rescue Exception => e 53 | m.reply e.to_s, true 54 | end 55 | else 56 | m.reply "#{self.class.name}: Internal error (missing API key)." 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /plugins/merriam_webster.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # merriam_webster.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Merriam-Webster interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "nokogiri" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class MerriamWebster < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["MERRIAM_WEBSTER_API_KEY"] 22 | URL = "http://www.dictionaryapi.com/api/v1/references/collegiate/xml/%{query}?key=%{key}" 23 | 24 | def usage 25 | "!define - Get the Merriam-Webster defintion of ." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?define$/ 30 | end 31 | 32 | match /define (\S+)/, method: :define_word, strip_colors: true 33 | 34 | def define_word(m, word) 35 | if KEY 36 | query = Addressable::URI.encode_component(word) 37 | url = URL % { query: query, key: KEY } 38 | 39 | begin 40 | xml = Nokogiri::XML(URI.open(url).read) 41 | 42 | def_elem = xml.xpath("//entry_list/entry/def/dt").first 43 | 44 | if def_elem 45 | definition = def_elem.text.delete(":") 46 | 47 | m.reply "#{word} - #{definition}.", true 48 | else 49 | m.reply "No definition for #{word}.", true 50 | end 51 | rescue Exception => e 52 | m.reply e.to_s, true 53 | end 54 | else 55 | m.reply "Internal error (missing API key)." 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /old_plugins/air_quality.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bot_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves air quality info for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "json" 12 | require "open-uri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class AirQuality < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | KEY = ENV["AIRNOW_API_KEY"] 21 | URL = "https://www.airnowapi.org/aq/forecast/zipCode/?" \ 22 | "format=application/json&" \ 23 | "zipCode=%{zip}&" \ 24 | "API_KEY=%{key}" 25 | 26 | def usage 27 | "!aq - Retrieve the air quality index for the given zip code." 28 | end 29 | 30 | def match?(cmd) 31 | cmd =~ /^(!)?aq$/ 32 | end 33 | 34 | match /aq (\d{5})/, method: :air_quality 35 | 36 | def air_quality(m, zip) 37 | if KEY 38 | url = URL % { key: KEY, zip: zip } 39 | 40 | begin 41 | hash = JSON.parse(open(url).read).first 42 | 43 | if hash 44 | region = hash["ReportingArea"] 45 | category = hash["Category"]["Name"] 46 | param = hash["ParameterName"] 47 | aqi = hash["AQI"] 48 | 49 | m.reply "#{region} is #{category} with an AQI of #{aqi} (#{param}).", true 50 | else 51 | m.reply "#{zip} is not a valid ZIP code.", true 52 | end 53 | rescue Exception => e 54 | m.reply e.to_s 55 | end 56 | else 57 | m.reply "#{self.class.name}: Internal error (missing API key)." 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /plugins/shakespearean_insults/shakespearean_insults.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # shakespearean_insults.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Shakespearean insults for yossarian-bot. 7 | # shakespearean_insults.yml taken from 'insult-generator' by Arvind Kunday. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "yaml" 13 | 14 | require_relative "../yossarian_plugin" 15 | 16 | class ShakespeareanInsults < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | INSULTS_FILE = File.expand_path(File.join(File.dirname(__FILE__), "shakespearean_insults.yml")) 21 | INSULTS = YAML.load_file(INSULTS_FILE) 22 | 23 | def usage 24 | "!insult [nick] - Generate a Shakespearean insult, and insult someone if given." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?insult$/ 29 | end 30 | 31 | match /insult$/, method: :insult 32 | 33 | def insult(m) 34 | col1 = INSULTS["column1"].sample 35 | col2 = INSULTS["column2"].sample 36 | col3 = INSULTS["column3"].sample 37 | 38 | m.reply "Thou art a #{col1}, #{col2} #{col3}!", true 39 | end 40 | 41 | match /insult (\S+)/, method: :insult_nick, strip_colors: true 42 | 43 | def insult_nick(m, nick) 44 | if m.channel.has_user?(nick) 45 | col1 = INSULTS["column1"].sample 46 | col2 = INSULTS["column2"].sample 47 | col3 = INSULTS["column3"].sample 48 | 49 | m.reply "#{nick} is a #{col1}, #{col2} #{col3}!" 50 | else 51 | m.reply "I don\'t see #{nick} in this channel." 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /plugins/stock_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # stock_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Yahoo! Finanance stock quotes for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "open-uri" 13 | require "json" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class StockQuotes < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=%{ticker}&apikey=%{api_key}" 22 | 23 | def usage 24 | "!stock - Retrieve a stock quote for the given ticker symbol." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?stock$/ 29 | end 30 | 31 | match /stock (\w+)$/, method: :stock_quote, strip_colors: true 32 | 33 | def stock_quote(m, symbol) 34 | symbol = Addressable::URI.encode_component(symbol) 35 | url = URL % { ticker: symbol, api_key: ENV["ALPHA_VANTAGE_API_KEY"] } 36 | 37 | begin 38 | res = JSON.parse(URI.open(url).read) 39 | 40 | if res["Error Message"] 41 | m.reply res["Error Message"], true 42 | else 43 | # Format the absolutely horrible API response 44 | res = res.values.first.transform_keys! { |k| k.split(" ").last } 45 | tick = res["symbol"] 46 | price = res["price"].to_f 47 | percent = res["percent"] 48 | 49 | m.reply "#{tick} - Trading at $#{price} (#{percent})", true 50 | end 51 | rescue Exception => e 52 | m.reply e.to_s, true 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /plugins/youtube_search.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # youtube_search.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides YouTube interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class YouTubeSearch < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=%{query}&key=%{key}" 22 | 23 | def usage 24 | "!yt - Search YouTube. Alias: !youtube." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?(youtube)|(yt)$/ 29 | end 30 | 31 | match /yt (.+)/, method: :youtube_search, strip_colors: true 32 | match /youtube (.+)/, method: :youtube_search, strip_colors: true 33 | 34 | def youtube_search(m, search) 35 | query = Addressable::URI.encode_component(search) 36 | url = URL % { query: query, key: ENV["YOUTUBE_API_KEY"] } 37 | 38 | begin 39 | hash = JSON.parse(URI.open(url).read) 40 | hash.default = "?" 41 | 42 | if hash["items"].nonempty? 43 | entry = hash["items"].first 44 | title = entry["snippet"]["title"] 45 | uploader = entry["snippet"]["channelTitle"] 46 | video_id = entry["id"]["videoId"] 47 | 48 | m.reply "#{title} [#{uploader}] - https://youtu.be/#{video_id}", true 49 | else 50 | m.reply "No results for #{search}.", true 51 | end 52 | rescue Exception => e 53 | m.reply e.to_s, true 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /old_plugins/google_search.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # google_search.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides Google interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class GoogleSearch < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | URL = "https://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=small&safe=active&q=%{query}&max-results=1&v=2&alt=json" 22 | 23 | def usage 24 | "!g - Search Google. Alias: !google." 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?g(?:oogle)?$/ 29 | end 30 | 31 | match /g(?:oogle)? (.+)/, method: :google_search, strip_colors: true 32 | 33 | def google_search(m, search) 34 | query = Addressable::URI.encode(search) 35 | url = URL % { query: query } 36 | 37 | begin 38 | hash = JSON.parse(open(url).read) 39 | 40 | if hash["responseData"]["results"].nonempty? 41 | site = URI.unescape(hash["responseData"]["results"].first["url"]) 42 | content = hash["responseData"]["results"].first["content"].gsub(/([\t\r\n])|(<(\/)?b>)/, "") 43 | content.gsub!(/(&)|(")|(<)|(>)|(')/, "&" => "&", """ => '"', "<" => "<", ">" => ">", "'" => "'") 44 | m.reply "#{site} - #{content}", true 45 | else 46 | m.reply "No Google results for #{search}.", true 47 | end 48 | rescue Exception => e 49 | m.reply e.to_s, true 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /plugins/omdb.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # -*- coding: utf-8 -*- 4 | # omdb.rb 5 | # Author: William Woodruff 6 | # ------------------------ 7 | # A Cinch plugin that gets movie/show info from the OMDB for yossarian-bot. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "addressable/uri" 13 | require "json" 14 | require "open-uri" 15 | 16 | require_relative "yossarian_plugin" 17 | 18 | class OMDB < YossarianPlugin 19 | include Cinch::Plugin 20 | use_blacklist 21 | 22 | URL = "https://www.omdbapi.com/?t=%s&plot=short&r=json&apikey=%s" 23 | 24 | def usage 25 | "!omdb - Look up a movie or show on the Open Movie Database." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?omdb$/ 30 | end 31 | 32 | match /omdb (.+)/, method: :omdb_search, strip_colors: true 33 | 34 | def omdb_search(m, title) 35 | query = Addressable::URI.encode_component(title) 36 | url = URL % { query: query, key: ENV["OMDB_API_KEY"] } 37 | 38 | begin 39 | hash = JSON.parse(URI.open(url).read) 40 | hash.default = "?" 41 | 42 | if !hash.key?("Error") 43 | title = hash["Title"] 44 | year = hash["Year"] 45 | genres = hash["Genre"] 46 | plot = hash["Plot"] 47 | imdb_rating = hash["imdbRating"] 48 | imdb_link = "http://imdb.com/title/#{hash["imdbID"]}" 49 | 50 | m.reply "#{title} (#{year}) (#{genres}). #{plot} IMDB rating: #{imdb_rating}/10. More at #{imdb_link}.", true 51 | else 52 | m.reply "Error: #{hash["Error"]}", true 53 | end 54 | rescue Exception => e 55 | m.reply e.to_s, true 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /plugins/cute_faces.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # cute_faces.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides cute faces for yossarian-bot. 7 | # Inspired by and derived from cybot's .cute command. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "yossarian_plugin" 13 | 14 | class CuteFaces < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | FACES = [ 19 | "(✿◠‿◠)っ~ ♥ %{nick}", "⊂◉‿◉つ ❤ %{nick}", "( ´・‿-) ~ ♥ %{nick}", 20 | "(っ⌒‿⌒)っ~ ♥ %{nick}", "ʕ´•ᴥ•`ʔσ” BEARHUG %{nick}", 21 | "%{user} ~(=^・ω・^)ヾ(^^ ) %{nick}", 22 | "%{user} (◎`・ω・´)人(´・ω・`*) %{nick}", 23 | "%{user} (*´・ω・)ノ(-ω-`*) %{nick}", "%{user} (ɔ ˘⌣˘)˘⌣˘ c) %{nick}", 24 | "%{nick} (´ε` )♡", "(⊃。•́‿•̀。)⊃ U GONNA GET HUGGED %{nick}", 25 | "%{user} (◦˘ З(◦’ںˉ◦)♡ %{nick}", "( ^◡^)っ~ ❤ %{nick}" 26 | ] 27 | 28 | def usage 29 | "!cute <nick> - Send a cute face to the given nick." 30 | end 31 | 32 | def match?(cmd) 33 | cmd =~ /^(!)?cute$/ 34 | end 35 | 36 | match /cute (\S+)/, method: :cute, strip_colors: true 37 | 38 | def cute(m, nick) 39 | if m.channel.has_user?(nick) 40 | if m.user.nick.downcase != nick.downcase 41 | if @bot.nick.downcase != nick.downcase 42 | cute = FACES.sample % {user: m.user.nick, nick: nick} 43 | m.reply cute 44 | else 45 | m.reply "I am incapable of self-love.", true 46 | end 47 | else 48 | m.reply "I can\'t let you do that.", true 49 | end 50 | else 51 | m.reply "I don\'t see #{nick} in this channel.", true 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /plugins/ctcp_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ctcp_version.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that sends CTCP VERSION requests to users. 7 | # Original authors: Lee Jarvis, Dominik Honnef 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "yossarian_plugin" 13 | 14 | class CTCPVersion < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | def initialize(*args) 19 | super 20 | @nick = "" 21 | @channel = "" 22 | @sent = false 23 | end 24 | 25 | def usage 26 | "!ver <nick> - Send a CTCP VERSION request to <nick>. Alias: !version." 27 | end 28 | 29 | def match?(cmd) 30 | cmd =~ /^(!)?ver(?:sion)?$/ 31 | end 32 | 33 | match /ver(?:sion)? (\S+)/, method: :ctcp_ver_req 34 | 35 | def ctcp_ver_req(m, nick) 36 | if m.channel.has_user?(nick) 37 | if nick == @bot.nick 38 | m.reply "See !botinfo version for my version.", true 39 | else 40 | User(nick).ctcp "VERSION" 41 | @nick = m.user.nick 42 | @channel = m.channel 43 | @sent = true 44 | end 45 | else 46 | m.reply "I don\'t see #{nick} in this channel." 47 | end 48 | end 49 | 50 | listen_to :ctcp, method: :ctcp_ver_recv 51 | 52 | def ctcp_ver_recv(m) 53 | if m.ctcp_message.include?("VERSION") 54 | if @sent 55 | version = Sanitize(m.ctcp_message.sub("VERSION ", "")) 56 | Channel(@channel).send "#{@nick}: #{m.user.nick} is using #{version}." 57 | @sent = false 58 | else 59 | m.reply "See !botinfo version for my version.", true 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /plugins/ip_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ip_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides IP address information lookup for yossarian-bot. 7 | # Uses the ipinfo.io service: https://ipinfo.io 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "addressable/uri" 13 | require "resolv" 14 | require "json" 15 | require "open-uri" 16 | 17 | require_relative "yossarian_plugin" 18 | 19 | class IPInfo < YossarianPlugin 20 | include Cinch::Plugin 21 | use_blacklist 22 | 23 | URL = "http://ipinfo.io/%{ip}/json" 24 | 25 | def usage 26 | "!ipinfo <ip> - Look up information about the given IP." 27 | end 28 | 29 | def match?(cmd) 30 | cmd =~ /^(!)?ipinfo$/ 31 | end 32 | 33 | match /ipinfo (.+)/, method: :ip_info, strip_colors: true 34 | 35 | def ip_info(m, ip) 36 | if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex 37 | url = URL % { ip: Addressable::URI.encode_component(ip) } 38 | 39 | begin 40 | hash = JSON.parse(URI.open(url).read) 41 | hash.default = "?" 42 | 43 | if !hash.key?("bogon") 44 | host = hash["hostname"] 45 | city = hash["city"] 46 | region = hash["region"] 47 | country = hash["country"] 48 | org = hash["org"] 49 | 50 | m.reply "#{ip} (#{host}) - Owner: #{org} - City: #{city}, Region: #{region}, Country: #{country}.", true 51 | else 52 | m.reply "#{ip} is a bogon.", true 53 | end 54 | rescue Exception => e 55 | m.reply e.to_s, true 56 | end 57 | else 58 | m.reply "\'#{ip}\' is not a valid IP.", true 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /plugins/reminders.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # reminders.rb 4 | # Authors: slackR (slackErEhth77) and William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin to set reminders for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by slackErEhth77 under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Reminders < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | def initialize(*args) 18 | super 19 | @threads = 0 20 | end 21 | 22 | def usage 23 | "!remind <count> <unit> <message> - Set a reminder message for a time in the future." 24 | end 25 | 26 | def match(cmd) 27 | cmd =~ /^(!)?remind$/ 28 | end 29 | 30 | match /remind (\d+) (\w+) (.+)/, method: :set_reminder 31 | 32 | def set_reminder(m, count, unit, msg) 33 | msg = Sanitize(msg) 34 | count = Integer(count).abs 35 | 36 | case unit 37 | when /^sec/ 38 | secs = count 39 | when /^min/ 40 | secs = count * 60 41 | when /^hour/ 42 | secs = count * 3600 43 | when /^day/ 44 | secs = count * 86400 45 | else 46 | m.reply "'#{unit}' is not one of my units. Try sec(s), min(s), or hour(s).", true 47 | return 48 | end 49 | 50 | if secs <= 604800 && @threads < 5 51 | m.reply "I'll tell you about #{msg} in #{secs} second(s).", true 52 | 53 | Thread.new do 54 | @threads += 1 55 | sleep secs 56 | m.reply "#{msg}", true 57 | @threads -= 1 58 | end.join 59 | elsif @threads >= 5 60 | m.reply "I already have a maximum number of reminders pending.", true 61 | else 62 | m.reply "Reminders longer than 7 days are not allowed.", true 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /plugins/beer_search.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # beer_search.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that BreweryDB interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class BeerSearch < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["BREWERYDB_API_KEY"] 22 | URL = "http://api.brewerydb.com/v2/beers?key=%{key}&name=%{query}" 23 | 24 | def usage 25 | "!beer <name> - Search for beer information on BreweryDB." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?beer$/ 30 | end 31 | 32 | match /beer (.+)/, method: :beer_search, strip_colors: true 33 | 34 | def beer_search(m, search) 35 | if KEY 36 | query = Addressable::URI.encode_component(search) 37 | url = URL % { key: KEY, query: query } 38 | 39 | begin 40 | hash = JSON.parse(URI.open(url).read) 41 | 42 | if !hash["data"].nil? 43 | beer = hash["data"].first 44 | name = beer["name"] 45 | abv = beer["abv"] || "?" 46 | ibu = beer["ibu"] || "?" 47 | type = beer["style"]["name"] || "?" 48 | desc = beer["style"]["description"] || "?" 49 | 50 | m.reply "#{name} (ABV: #{abv}, IBU: #{ibu}) - #{type} - #{desc}", true 51 | else 52 | m.reply "Couldn't find '#{search}' on BreweryDB.", true 53 | end 54 | rescue Exception => e 55 | m.reply e.to_s, true 56 | end 57 | else 58 | m.reply "Internal error (missing API key)." 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /old_plugins/jerkcity.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # jerkcity.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that fetches random Jerkcity comics for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "open-uri" 12 | require "nokogiri" 13 | 14 | require_relative "yossarian_plugin" 15 | 16 | class Jerkcity < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | URL = "https://jerkcity.com" 21 | 22 | def initialize(*args) 23 | super 24 | @comic_count = 0 25 | initialize_comic_count 26 | end 27 | 28 | def usage 29 | "!jerkcity - Fetch a random comic from jerkcity.com." 30 | end 31 | 32 | def match?(cmd) 33 | cmd =~ /^(!)?jerkcity$/ 34 | end 35 | 36 | def initialize_comic_count 37 | html = Nokogiri::HTML(URI.open(URL).read) 38 | text = html.css("div")[3].text 39 | @comic_count = text.split("|")[2].strip.gsub(/(No. )|(,)/, "").to_i 40 | rescue Exception => e 41 | debug e.to_s 42 | end 43 | 44 | match /jerkcity$/, method: :jerkcity 45 | 46 | def jerkcity(m) 47 | if @comic_count.positive? 48 | rand = Random.rand(1..@comic_count) 49 | comic_url = "#{URL}/_jerkcity#{rand}.html" 50 | 51 | begin 52 | html = Nokogiri::HTML(URI.open(comic_url).read) 53 | text_array = html.css("div")[3].text.split("|") 54 | comic_desc = text_array[0].strip 55 | comic_date = text_array[1].strip 56 | 57 | m.reply "#{comic_desc} (#{comic_date}) - #{comic_url}", true 58 | rescue Exception => e 59 | m.reply e.to_s, true 60 | end 61 | else 62 | m.reply "Internal error (couldn't get comic count)." 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /plugins/web_search.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # web_search.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides search engine interaction for yossarian-bot. 7 | # Google is used as the default engine. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "addressable/uri" 13 | require "json" 14 | require "open-uri" 15 | require "sanitize" 16 | 17 | require_relative "yossarian_plugin" 18 | 19 | class WebSearch < YossarianPlugin 20 | include Cinch::Plugin 21 | use_blacklist 22 | 23 | URL = "https://www.googleapis.com/customsearch/v1?key=%<key>s&cx=%<cx>s&q=%<query>s" 24 | KEY = ENV["GOOGLE_SEARCH_API_KEY"] 25 | ENGINE_ID = ENV["GOOGLE_SEARCH_ENGINE_ID"] 26 | 27 | def usage 28 | "!g <search> - Search the web with Google." 29 | end 30 | 31 | def match?(cmd) 32 | cmd =~ /^(!)?g$/ 33 | end 34 | 35 | match /g (.+)/, method: :google_search, strip_colors: true 36 | 37 | def google_search(m, search) 38 | unless KEY && ENGINE_ID 39 | m.reply "#{self.class.name}: Internal error (missing API keys)." 40 | return 41 | end 42 | 43 | query = Addressable::URI.encode_component(search) 44 | url = URL % { key: KEY, cx: ENGINE_ID, query: query } 45 | 46 | begin 47 | hsh = JSON.parse(URI.open(url).read) 48 | 49 | result = hsh["items"]&.first 50 | 51 | if result 52 | site = result["link"] 53 | content = Sanitize.clean(result["snippet"]).normalize_whitespace 54 | 55 | m.reply "#{site} - #{content}", true 56 | else 57 | m.reply "No Google results for '#{search}'.", true 58 | end 59 | rescue Exception => e 60 | m.reply e.to_s, true 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /plugins/ping.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ping.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A canonical Cinch plugin for yossarian-bot. 7 | # Responds to a user's 'ping' with a 'pong'. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require_relative "yossarian_plugin" 13 | 14 | class Ping < YossarianPlugin 15 | include Cinch::Plugin 16 | use_blacklist 17 | 18 | def initialize(*args) 19 | super 20 | @nick = "" 21 | @channel = "" 22 | @timestamp = {} 23 | @thread = Thread.current 24 | @thread[:ping_sent] = false 25 | end 26 | 27 | def usage 28 | "!ping - Ping the bot for a timestamped response." 29 | end 30 | 31 | def match?(cmd) 32 | cmd =~ /^(!)?ping$/ 33 | end 34 | 35 | def format_ping_time 36 | milis = (Time.now - @timestamp) * 1000.0 37 | mins = (milis / 60000).to_i 38 | secs = (milis / 1000).to_i 39 | str = "" 40 | if mins > 0 41 | str = "#{str}#{mins} minute(s), " 42 | end 43 | if secs > 0 44 | str = "#{str}#{secs} second(s), " 45 | end 46 | "#{str}#{((milis%60000)%1000).to_i} millisecond(s)" 47 | end 48 | 49 | match /ping$/, method: :ctcp_ping_req 50 | listen_to :ctcp, method: :ctcp_ping_recv 51 | 52 | def ctcp_ping_req(m) 53 | @nick = m.user.nick 54 | @channel = m.channel 55 | @timestamp = Time.now 56 | User(@nick).ctcp "PING #{@timestamp.to_i}" 57 | @thread[:ping_sent] = true 58 | end 59 | 60 | def ctcp_ping_recv(m) 61 | if m.ctcp_message.include?("PING") 62 | if @thread[:ping_sent] 63 | Channel(@channel).send "#{@nick}: pong #{@timestamp.to_i} (#{format_ping_time})" 64 | @thread[:ping_sent] = false 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /plugins/link_titling.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # link_titling.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that autotitles any links sniffed by yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "uri" 12 | require "open-uri" 13 | require "open_uri_redirections" 14 | require "nokogiri" 15 | require "timeout" 16 | 17 | require_relative "yossarian_plugin" 18 | 19 | class LinkTitling < YossarianPlugin 20 | include Cinch::Plugin 21 | use_blacklist 22 | 23 | YOUTUBE_KEY = ENV["YOUTUBE_API_KEY"] 24 | YOUTUBE_URL = "https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics,contentDetails&id=%{id}&key=%{key}" 25 | 26 | match /(#{URI.regexp(['http', 'https'])})/, use_prefix: false, method: :link_title 27 | 28 | def link_title(m, link) 29 | uri = URI(link) 30 | 31 | case uri.host 32 | when /youtube.com/, /youtu.be/ 33 | title = youtube_title(uri) 34 | else 35 | title = generic_title(uri) 36 | end 37 | 38 | m.reply "Title: #{title}" if title && !title.empty? 39 | end 40 | 41 | def generic_title(uri) 42 | Timeout.timeout(5) do 43 | html = Nokogiri::HTML(URI.open(uri, allow_redirections: :safe)) 44 | html.css("title").text.normalize_whitespace 45 | end 46 | rescue Exception 47 | "Unknown" 48 | end 49 | 50 | def youtube_title(uri) 51 | query = uri.query || "" 52 | id = URI.decode_www_form(query).to_h["v"] 53 | 54 | if id.nil? 55 | generic_title(uri) 56 | else 57 | api_url = YOUTUBE_URL % { id: id, key: YOUTUBE_KEY } 58 | 59 | begin 60 | hash = JSON.parse(URI.open(api_url).read)["items"].first 61 | hash["snippet"]["title"] 62 | rescue Exception => e 63 | "Unknown" 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /config.yml.example: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | environment: 4 | # each of the following is optional, but not all plugins will function 5 | # with them missing. 6 | WOLFRAM_ALPHA_APPID_KEY: api_key 7 | WUNDERGROUND_API_KEY: api_key 8 | MERRIAM_WEBSTER_API_KEY: api_key 9 | YOUTUBE_API_KEY: api_key 10 | LASTFM_API_KEY: api_key 11 | LASTFM_API_SECRET: api_secret 12 | OEX_API_KEY: api_key 13 | GIPHY_API_KEY: api_key 14 | GOODREADS_API_KEY: api_key 15 | BREWERYDB_API_KEY: api_key 16 | AIRNOW_API_KEY: api_key 17 | MAL_USERNAME: username 18 | MAL_PASSWORD: password 19 | OMDB_API_KEY: api_key 20 | COINMARKETCAP_API_KEY: api_key 21 | ALPHA_VANTAGE_API_KEY: api_key 22 | # mandatory 23 | servers: 24 | # at least one server entry is mandatory 25 | irc.rizon.net: 26 | # optional, used to ensure a stable ID when the IRC server's domain or IP changes 27 | server_id: irc.rizon.net 28 | # optional 29 | nick: 'yossarian-bot' 30 | # optional, needed if the nick is registered 31 | auth: 32 | type: 'nickserv' 33 | password: 'example' 34 | # optional 35 | prefix: '^!' 36 | # optional 37 | admins: 38 | - 'cpt_yossarian' 39 | - 'yossarian' 40 | # optional 41 | ssl: true 42 | # optional 43 | port: 6697 44 | # mandatory 45 | channels: 46 | - '#yossarian-bot' 47 | # mandatory - use an empty array if none 48 | disabled_plugins: 49 | - 'LinkTitling' 50 | - 'UserIntros' 51 | - 'RegexReplace' 52 | # optional 53 | blacklist: 54 | - 'spammer1' 55 | irc.freenode.net: 56 | nick: 'yossarian-bot' 57 | prefix: '^!' 58 | admins: 59 | - 'woodruffw' 60 | ssl: true 61 | port: 6697 62 | channels: 63 | - '#bots' 64 | disabled_plugins: [] 65 | -------------------------------------------------------------------------------- /plugins/bot_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bot_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that retrieves info on yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "time_difference" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class BotInfo < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def usage 20 | "!bi [key] - Retrieve information about the bot. See link in !help for keys. Alias: !botinfo." 21 | end 22 | 23 | def match?(cmd) 24 | cmd =~ /^(!)?(botinfo$)|(bi$)/ 25 | end 26 | 27 | match /bi (\w+)/, method: :bot_info 28 | match /botinfo (\w+)/, method: :bot_info 29 | 30 | def bot_info(m, key) 31 | case key 32 | when /(^version$)|(^vers?$)/ 33 | m.reply "yossarian-bot #{@bot.version} running on grinch #{Cinch::VERSION} with ruby #{RUBY_VERSION} (#{RUBY_PLATFORM})." 34 | when /(^source$)|(^src$)/ 35 | m.reply "yossarian-bot's source code can be found here: https://git.io/vs65u" 36 | when /(^contrib)|(^todo$)/ 37 | m.reply "Want to contribute? Here are some things to do: https://git.io/vwTmA" 38 | when /^author$/ 39 | m.reply "Author: William Woodruff (woodruffw, yossarian)" 40 | when /^uptime$/ 41 | diff = TimeDifference.between(@bot.starttime, Time.now).in_general 42 | m.reply "I\'ve been online for %{days} days, %{hours} hours, %{minutes} minutes, and %{seconds} seconds." % diff 43 | when /^chan(nel)?s$/ 44 | m.reply "Channels: %s" % @bot.channels.join(", ") 45 | when /^admins$/ 46 | # avoid mass highlights 47 | m.user.notice "Admins: %s" % @bot.admins.join(", ") 48 | when /^ignores$/ 49 | # avoid mass highlights 50 | m.user.notice "Ignored set: %s" % @bot.blacklist.to_a.join(", ") 51 | else 52 | m.reply "I don\'t have any information on \'#{key}\'. Try !help botinfo." 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /plugins/myanimelist.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # -*- coding: utf-8 -*- 4 | # myanimelist.rb 5 | # Author: Winston Weinert 6 | # ------------------------ 7 | # A Cinch plugin that provides MyAnimeList search interaction for yossarian-bot. 8 | # ------------------------ 9 | # This code is licensed by Winston Weinert under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "htmlentities" 13 | require "myanimelist" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class MyAnimeListSearch < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | def initialize(*args) 22 | super 23 | MyAnimeList.configure do |cfg| 24 | cfg.username = ENV["MAL_USERNAME"] 25 | cfg.password = ENV["MAL_PASSWORD"] 26 | end 27 | @entities = HTMLEntities.new 28 | end 29 | 30 | def usage 31 | "!<anime|manga> <search> - Search MyAnimeList for anime or manga." 32 | end 33 | 34 | def match?(cmd) 35 | cmd =~ /^(!)?(anime|manga)$/ 36 | end 37 | 38 | match /(anime|manga) (.+)/, method: :search, strip_colors: true 39 | 40 | def search(m, type, query) 41 | begin 42 | res = type == "anime" ? MyAnimeList.search_anime(query) : MyAnimeList.search_manga(query) 43 | rescue MyAnimeList::ApiException 44 | res = nil 45 | end 46 | 47 | if res&.any? 48 | first = res.is_a?(Hash) ? res : res.first; # Hash of one result or Array of many results 49 | 50 | url = "http://myanimelist.net/#{type}/#{first["id"]}" 51 | title = first["title"].strip 52 | 53 | syn = @entities.decode(first["synopsis"]).strip 54 | truncated = syn[0..147].strip 55 | syn = "#{truncated}..." if syn != truncated 56 | 57 | if first["english"]&.is_a? String # Empty Hash or String 58 | english = first["english"].strip 59 | maybe_english = english != title ? " (#{english})" : "" 60 | else 61 | maybe_english = "" 62 | end 63 | 64 | m.reply "#{url} #{title}#{maybe_english} -- #{syn}", true 65 | else 66 | m.reply "No results for #{type} `#{query}'", true 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /plugins/phone_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # phone_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides phone number information lookup for yossarian-bot. 7 | # Uses the numverify service: https://numverify.com/ 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "nokogiri" 13 | require "json" 14 | require "open-uri" 15 | require "digest/md5" 16 | 17 | require_relative "yossarian_plugin" 18 | 19 | class PhoneInfo < YossarianPlugin 20 | include Cinch::Plugin 21 | use_blacklist 22 | 23 | URL = "https://numverify.com/php_helper_scripts/phone_api.php?secret_key=%{secret}&number=%{number}" 24 | 25 | def usage 26 | "!phoneinfo <number> - Look up information about the given phone number." 27 | end 28 | 29 | def match?(cmd) 30 | cmd =~ /^(!)?phoneinfo$/ 31 | end 32 | 33 | match /phoneinfo (\d+)/, method: :phone_info, strip_colors: true 34 | 35 | def get_secret(number) 36 | html = Nokogiri::HTML(URI.open("https://numverify.com").read) 37 | request_secret = html.css("[name=\"scl_request_secret\"]").first.attr "value" 38 | # lol 39 | Digest::MD5.hexdigest("#{number}#{request_secret}") 40 | end 41 | 42 | def phone_info(m, number) 43 | secret = get_secret number 44 | url = URL % { secret: secret, number: number } 45 | 46 | begin 47 | hash = JSON.parse(URI.open(url).read) 48 | hash.delete_if { |_, v| v.is_a?(String) && v.empty? } 49 | hash.default = "Unknown" 50 | 51 | if hash["valid"] 52 | number_fmt = hash["international_format"] 53 | type = hash["line_type"] 54 | carrier = hash["carrier"] 55 | location = hash["location"] 56 | country = hash["country_name"] 57 | 58 | m.reply "#{number_fmt} - Line Type: #{type} - Carrier: #{carrier} - Location: #{location}, #{country}.", true 59 | else 60 | m.reply "#{number} is not a valid phone number.", true 61 | end 62 | rescue Exception => e 63 | m.reply e.to_s, true 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /plugins/last_seen/last_seen.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # last_seen.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that keeps track of when users leave. 7 | # Based on cinch/examples/plugins/seen.rb. 8 | # Original authors: Lee Jarvis, Dominik Honnef 9 | # ------------------------ 10 | # This code is licensed by William Woodruff under the MIT License. 11 | # http://opensource.org/licenses/MIT 12 | 13 | require "yaml" 14 | require "fileutils" 15 | 16 | require_relative "../yossarian_plugin" 17 | 18 | class LastSeen < YossarianPlugin 19 | include Cinch::Plugin 20 | use_blacklist 21 | 22 | class LastSeenStruct < Struct.new(:who, :where, :what, :time) 23 | def to_s 24 | "#{who} was last seen on #{time.asctime} in #{where} saying #{what}" 25 | end 26 | end 27 | 28 | def initialize(*args) 29 | super 30 | @users_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "last_seen.yml")) 31 | 32 | if File.file?(@users_file) 33 | @users = YAML.load_file(@users_file, permitted_classes: [LastSeenStruct, Symbol, Time]) 34 | else 35 | FileUtils.mkdir_p File.dirname(@users_file) 36 | @users = {} 37 | end 38 | end 39 | 40 | def sync_users_file 41 | File.open(@users_file, "w+") do |file| 42 | file.write @users.to_yaml 43 | end 44 | end 45 | 46 | def usage 47 | "!seen <nick> - Check the last time <nick> was seen." 48 | end 49 | 50 | def match?(cmd) 51 | cmd =~ /^(!)?seen$/ 52 | end 53 | 54 | listen_to :channel 55 | 56 | def listen(m) 57 | @users[m.user.nick.downcase] = LastSeenStruct.new(m.user.nick, m.channel.to_s, m.message, Time.now) 58 | sync_users_file 59 | end 60 | 61 | match /seen (\S+)/, method: :last_seen, strip_colors: true 62 | 63 | def last_seen(m, nick) 64 | if nick.downcase == @bot.nick.downcase 65 | m.reply "That\'s not going to work.", true 66 | elsif nick.downcase == m.user.nick.downcase 67 | m.reply "You\'re online right now.", true 68 | elsif @users.key?(nick.downcase) 69 | m.reply @users[nick.downcase].to_s, true 70 | else 71 | m.reply "I\'ve never seen #{nick}.", true 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /plugins/zalgo/zalgo_text.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | 3 | # zalgo_text.rb 4 | # Author: Alex Young 5 | # Modified by: William Woodruff 6 | # ------------------------ 7 | # Generates Zalgo text for yossarian-bot 8 | # ------------------------ 9 | # No license, as far as I can tell. 10 | 11 | module ZalgoText 12 | module Characters 13 | def self.up 14 | %w{ 15 | ̍ ̎ ̄ ̅ 16 | ̿ ̑ ̆ ̐ 17 | ͒ ͗ ͑ ̇ 18 | ̈ ̊ ͂ ̓ 19 | ̈ ͊ ͋ ͌ 20 | ̃ ̂ ̌ ͐ 21 | ̀ ́ ̋ ̏ 22 | ̒ ̓ ̔ ̽ 23 | ̉ ͣ ͤ ͥ 24 | ͦ ͧ ͨ ͩ 25 | ͪ ͫ ͬ ͭ 26 | ͮ ͯ ̾ ͛ 27 | ͆ ̚ 28 | } 29 | end 30 | 31 | def self.down 32 | %w{ 33 | ̖ ̗ ̘ ̙ 34 | ̜ ̝ ̞ ̟ 35 | ̠ ̤ ̥ ̦ 36 | ̩ ̪ ̫ ̬ 37 | ̭ ̮ ̯ ̰ 38 | ̱ ̲ ̳ ̹ 39 | ̺ ̻ ̼ ͅ 40 | ͇ ͈ ͉ ͍ 41 | ͎ ͓ ͔ ͕ 42 | ͖ ͙ ͚ ̣ 43 | } 44 | end 45 | 46 | def self.mid 47 | %w{ 48 | ̕ ̛ ̀ ́ 49 | ͘ ̡ ̢ ̧ 50 | ̨ ̴ ̵ ̶ 51 | ͏ ͜ ͝ ͞ 52 | ͟ ͠ ͢ ̸ 53 | ̷ ͡ ҉_ 54 | } 55 | end 56 | 57 | def self.all 58 | up + down + mid 59 | end 60 | 61 | def self.is_char?(char) 62 | all.find { |c| c == char } 63 | end 64 | end 65 | 66 | def self.he_comes(text, options = {}) 67 | result = "" 68 | options = { up: true, mid: true, down: true }.merge options 69 | 70 | text.each_char.each do |char| 71 | next if Characters.is_char? char 72 | 73 | result << char 74 | counts = { up: 0, mid: 0, down: 0 } 75 | 76 | case options[:size] 77 | when :mini 78 | counts[:up] = rand(8) 79 | counts[:mid] = rand(2) 80 | counts[:down] = rand(8) 81 | when :maxi 82 | counts[:up] = rand(16) + 3 83 | counts[:mid] = rand(4) + 1 84 | counts[:down] = rand(64) + 3 85 | else 86 | counts[:up] = rand(8) + 1 87 | counts[:mid] = rand(6) / 2 88 | counts[:down] = rand(8) + 1 89 | end 90 | 91 | [:up, :mid, :down].each do |d| 92 | counts[d].times { result << Characters.send(d)[rand Characters.send(d).size] } if options[d] 93 | end 94 | end 95 | 96 | result 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /plugins/weather.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # weather.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides APIXU weather interaction for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "net/http" 13 | require "json" 14 | require_relative "yossarian_plugin" 15 | 16 | class Weather < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | KEY = ENV["WEATHERSTACK_API_KEY"] 21 | 22 | def usage 23 | "!w <location> - Get the weather at <location>. Alias: !weather." 24 | end 25 | 26 | def match?(cmd) 27 | cmd =~ /^(!)?(weather)|(^w)$/ 28 | end 29 | 30 | match /w (.+)/, method: :weather, strip_colors: true 31 | match /weather (.+)/, method: :weather, strip_colors: true 32 | 33 | def weather(m, location) 34 | if KEY 35 | params = { 36 | :access_key => KEY, 37 | :query => location 38 | } 39 | uri = URI("http://api.weatherstack.com/current") 40 | uri.query = URI.encode_www_form(params) 41 | json = Net::HTTP.get(uri) 42 | hsh = JSON.parse(json) 43 | if hsh["location"] 44 | loc = hsh["location"]["name"] 45 | loc = "#{loc}, #{hsh["location"]["region"]}" 46 | loc = "#{loc}, #{hsh["location"]["country"]}" 47 | 48 | current = hsh["current"] 49 | weather = current["weather_descriptions"].first 50 | 51 | temp_c = current["temperature"] 52 | temp_f = temp_c * 1.8 + 32 53 | temp = "#{temp_c}°C (#{temp_f.round(2)}°F)" 54 | 55 | feels_like_c = current["feelslike"] 56 | feels_like_f = feels_like_c * 1.8 + 32 57 | feels_like = "#{feels_like_c}°C (#{feels_like_f.round(2)}°F)" 58 | 59 | humidity = current["humidity"] 60 | 61 | m.reply "Current temperature in #{loc} is #{temp} and #{weather}; " \ 62 | "feels like #{feels_like} with #{humidity}\% humidity.", true 63 | else 64 | m.reply "Nothing found for location '#{location}'.", true 65 | end 66 | else 67 | m.reply "Internal error (missing API key)." 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /plugins/crypto.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "open-uri" 4 | require "json" 5 | 6 | require_relative "yossarian_plugin" 7 | 8 | class Crypto < YossarianPlugin 9 | include Cinch::Plugin 10 | use_blacklist 11 | 12 | BASE_URL = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest" 13 | 14 | def usage 15 | "!crypto <coin> [currency] - Get the current price of a coin (converted to an optional currency)" 16 | end 17 | 18 | def match?(cmd) 19 | cmd =~ /^(!)?crypto\s(\w+)\s?(\w+)?$/ 20 | end 21 | 22 | match /crypto (.+)/, method: :crypto, strip_colors: true 23 | 24 | def crypto(m, payload) 25 | coin_name, currency = payload.split(" ").map(&:upcase) 26 | currency ||= "USD" 27 | api_endpoint = "#{BASE_URL}?symbol=#{coin_name}&convert=#{currency}" 28 | 29 | response = JSON.parse(URI.open( 30 | api_endpoint, 31 | "X-CMC_PRO_API_KEY" => ENV['COINMARKETCAP_API_KEY'] 32 | ).read) 33 | 34 | coin = parse_coin_info(response, coin_name, currency) 35 | 36 | m.reply "1 #{coin[:sym]} (#{coin[:name]}) = #{coin[:price].to_f.round(3)} #{coin[:currency].upcase} | #{fmt_changes(coin[:changes])}", true 37 | 38 | rescue OpenURI::HTTPError => e 39 | handle_error(m, e.io) 40 | rescue Exception => e 41 | m.reply e.to_s, true 42 | end 43 | 44 | private 45 | 46 | def fmt_changes(changes) 47 | changes.map do |k,v| 48 | direction = v.to_f > 0 ? "↑" : "↓" 49 | "#{k.to_s.capitalize} #{direction} #{v}%" 50 | end.join(" | ") 51 | end 52 | 53 | def parse_coin_info(response, coin_name, currency) 54 | hash = response["data"][coin_name] 55 | 56 | { 57 | name: hash["name"], 58 | sym: hash["symbol"], 59 | price: hash["quote"][currency]["price"], 60 | changes: { 61 | hourly: hash["quote"][currency]["percent_change_1h"], 62 | daily: hash["quote"][currency]["percent_change_24h"], 63 | weekly: hash["quote"][currency]["percent_change_7d"], 64 | }, 65 | currency: currency, 66 | } 67 | end 68 | 69 | def handle_error(m, status) 70 | hash = JSON.parse(status.string)["status"] 71 | code = hash["error_code"].to_i 72 | err = hash["error_message"] 73 | 74 | m.reply "[#{code}] #{err}", true 75 | end 76 | 77 | end 78 | -------------------------------------------------------------------------------- /plugins/user_mail/user_mail.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # user_mail.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides user mailboxes for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | require "fileutils" 13 | 14 | require_relative "../yossarian_plugin.rb" 15 | 16 | class UserMail < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | class MboxMessageStruct < Struct.new(:sender, :time, :message) 21 | def to_s 22 | stamp = time.strftime "%H:%M:%S" 23 | "[#{stamp}] <#{sender}> - #{message}" 24 | end 25 | end 26 | 27 | def initialize(*args) 28 | super 29 | @mbox_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "user_mail.yml")) 30 | 31 | if File.file?(@mbox_file) 32 | @mbox = YAML.load_file(@mbox_file, permitted_classes: [MboxMessageStruct, Time, Symbol]) 33 | @mbox.default_proc = Proc.new { |h, k| h[k] = [] } 34 | else 35 | FileUtils.mkdir_p File.dirname(@mbox_file) 36 | @mbox = Hash.new { |h, k| h[k] = [] } 37 | end 38 | end 39 | 40 | def sync_mbox_file 41 | File.open(@mbox_file, "w+") do |file| 42 | file.write @mbox.to_yaml 43 | end 44 | end 45 | 46 | def usage 47 | "!mail <nick> <message> - Send a message to a nick. Messages are delivered the next time the nick speaks." 48 | end 49 | 50 | def match?(cmd) 51 | cmd =~ /^(!)?mail$/ 52 | end 53 | 54 | listen_to :channel 55 | 56 | def listen(m) 57 | nick = m.user.nick.downcase 58 | 59 | if @mbox.key?(nick) 60 | m.user.send "Here is your mail. Use !mail <nick> <message> to reply in turn." 61 | 62 | @mbox[nick].each do |msg| 63 | m.user.send msg.to_s 64 | end 65 | @mbox.delete(nick) 66 | sync_mbox_file 67 | end 68 | end 69 | 70 | match /mail (\S+) (.+)/, method: :mail 71 | 72 | def mail(m, nick, msg) 73 | if nick.downcase == @bot.nick.downcase 74 | m.reply "That's not going to work.", true 75 | else 76 | @mbox[nick.downcase] << MboxMessageStruct.new(m.user.nick, Time.now, msg) 77 | 78 | m.reply "I\'ll give your message to #{nick} the next time I see them.", true 79 | sync_mbox_file 80 | end 81 | end 82 | end 83 | 84 | -------------------------------------------------------------------------------- /plugins/artist_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # artist_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that gets information about an artist from Last.fm. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "lastfm" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class ArtistInfo < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | KEY = ENV["LASTFM_API_KEY"] 20 | SECRET = ENV["LASTFM_API_SECRET"] 21 | 22 | def initialize(*args) 23 | super 24 | 25 | @lastfm = Lastfm.new(KEY, SECRET) if KEY && SECRET 26 | end 27 | 28 | def usage 29 | "!artist <artist> - Get information about an artist from Last.fm." 30 | end 31 | 32 | def match?(cmd) 33 | cmd =~ /^(!)?artist$/ 34 | end 35 | 36 | match /artist (.+)/, method: :artist_info, strip_colors: true 37 | 38 | def artist_info(m, artist) 39 | if @lastfm 40 | begin 41 | info = @lastfm.artist.get_info(artist: artist) 42 | 43 | if !info["mbid"]&.empty? 44 | info.default = "?" 45 | name = info["name"] 46 | url = info["url"] 47 | formed = info["bio"]["yearformed"] || "?" 48 | place = info["bio"]["placeformed"] || "?" 49 | 50 | tags = if info["tags"] && info["tags"]["tag"] 51 | info["tags"]["tag"].map do |tag| 52 | tag["name"].capitalize 53 | end.join(", ") 54 | else 55 | "None" 56 | end 57 | 58 | artists = if info["similar"] && info["similar"]["artist"] 59 | info["similar"]["artist"].map do |art| 60 | art["name"] 61 | end.join(", ") 62 | else 63 | "None" 64 | end 65 | 66 | m.reply "#{name} (formed #{formed}, #{place}): #{tags}. Similar artists: #{artists}. #{url}.", true 67 | else 68 | m.reply "No MBID found. Best guess: \'#{info['tags']['tag'][0]['name']}\'.", true 69 | end 70 | rescue Exception => e 71 | m.reply e.to_s.strip, true 72 | end 73 | else 74 | m.reply "#{self.class.name}: Internal error (missing API key(s))." 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /old_plugins/book_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # book_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that gets information about a book from Goodreads. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "goodreads" 12 | 13 | require_relative "yossarian_plugin" 14 | 15 | class BookInfo < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | KEY = ENV["GOODREADS_API_KEY"] 20 | 21 | def initialize(*args) 22 | super 23 | @goodreads = Goodreads.new(api_key: KEY) 24 | end 25 | 26 | def usage 27 | "!book <book> - Get information about a book from Goodreads." 28 | end 29 | 30 | def match?(cmd) 31 | cmd =~ /^(!)?book$/ 32 | end 33 | 34 | match /book (.+)/, method: :book_info, strip_colors: true 35 | 36 | def book_info(m, book) 37 | if KEY 38 | begin 39 | book_info = @goodreads.book_by_title(book) 40 | book_info.default = "?" 41 | 42 | title = book_info["title"] 43 | authors = book_info["authors"]["author"] 44 | 45 | authors = if authors.is_a? Array 46 | authors.map do |a| 47 | a["name"] 48 | end.join(", ") 49 | else 50 | authors["name"] 51 | end 52 | 53 | year = book_info["work"]["original_publication_year"] || book_info["publication_year"] 54 | rating = book_info["average_rating"] 55 | ratings_count = book_info["ratings_count"] 56 | link = book_info["link"] 57 | 58 | similar_books = book_info["similar_books"]["book"] 59 | 60 | similar_books = if similar_books 61 | similar_books[0...3].map do |b| 62 | b["title_without_series"] 63 | end.join(", ") 64 | else 65 | "None" 66 | end 67 | 68 | m.reply "#{title} (#{authors}, published #{year}). Rated #{rating}/5 by #{ratings_count} people. Similar books: #{similar_books}. More information at #{link}", true 69 | rescue Goodreads::NotFound 70 | m.reply "Goodreads has nothing for '#{book}'.", true 71 | rescue Exception => e 72 | m.reply e.to_s, true 73 | end 74 | else 75 | m.reply "#{self.class.name}: Internal error (missing API key)." 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) with restrictions 2 | 3 | Copyright (c) 2020 William Woodruff <william @ yossarian.net> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | The following terms additionally apply and override any above terms for 24 | applicable parties: 25 | 26 | You may not use, copy, modify, merge, publish, distribute, sublicense, 27 | and/or sell copies of the Software in a military or law enforcement context, 28 | defined as follows: 29 | 30 | 1. A military context is a professional context where the intended application 31 | of the Software is integration or use with or by military software, tools 32 | (software or hardware), or personnel. This includes contractors and 33 | subcontractors as well as research affiliates of any military organization. 34 | 35 | 2. A law enforcement context is a professional context where the intended 36 | application of the Software is integration or use with or by law enforcement 37 | software, tools (software or hardware), or personnel. This includes 38 | contractors and subcontractors as well as research affiliates of any law 39 | enforcement organization. 40 | 41 | Entities that sell or license to military or law enforcement organizations 42 | may use the Software under the original terms, but only in contexts that do 43 | not assist or supplement the sold or licensed product. 44 | 45 | Students and academics who are affiliated with research institutions may use 46 | the Software under the original terms, but only in contexts that do not assist 47 | or supplement collaboration or affiliation with any military or law 48 | enforcement organization. 49 | -------------------------------------------------------------------------------- /plugins/user_points/user_points.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # user_points.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides points for users for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | require "fileutils" 13 | 14 | require_relative "../yossarian_plugin" 15 | 16 | class UserPoints < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | def initialize(*args) 21 | super 22 | @points_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "user_points.yml")) 23 | 24 | if File.file?(@points_file) 25 | @points = YAML.load_file(@points_file) 26 | @points.default_proc = Proc.new { |h, k| h[k] = 0 } 27 | else 28 | FileUtils.mkdir_p File.dirname(@points_file) 29 | @points = Hash.new { |h, k| h[k] = 0 } 30 | end 31 | end 32 | 33 | def sync_points_file 34 | File.open(@points_file, "w+") do |file| 35 | file.write @points.to_yaml 36 | end 37 | end 38 | 39 | def usage 40 | "!point <command> <nick> - Give or take points away from a nickname. Commands are add, rm, and show." 41 | end 42 | 43 | def match?(cmd) 44 | cmd =~ /^(!)?point$/ 45 | end 46 | 47 | match /point add (\S+)/, method: :add_point 48 | 49 | def add_point(m, nick) 50 | nickd = nick.downcase 51 | 52 | if m.user.nick.downcase != nickd 53 | @points[nickd] += 1 54 | m.reply "#{nick} now has #{@points[nickd]} points.", true 55 | else 56 | @points[nickd] -= 1 57 | m.reply "Nice try. You now have #{@points[nickd]} points.", true 58 | end 59 | 60 | sync_points_file 61 | end 62 | 63 | match /point rm (\S+)/, method: :remove_point 64 | 65 | def remove_point(m, nick) 66 | nickd = nick.downcase 67 | 68 | @points[nickd] -= 1 69 | m.reply "#{nick} now has #{@points[nickd]} points.", true 70 | sync_points_file 71 | end 72 | 73 | match /point show (\S+)/, method: :show_intro 74 | 75 | def show_intro(m, nick) 76 | m.reply "#{nick} has #{@points[nick.downcase]} points.", true 77 | end 78 | 79 | match /point leaderboard/, method: :show_leaderboard 80 | 81 | def show_leaderboard(m) 82 | top5 = @points.max_by(5) { |_, p| p } 83 | 84 | leaderboard = if top5.empty? 85 | "Empty" 86 | else 87 | top5.map { |u, p| "#{u}: #{p}" }.join ", " 88 | end 89 | m.reply "Leaderboard: #{leaderboard}.", true 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /plugins/corona.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "open-uri" 4 | require "json" 5 | 6 | require_relative "yossarian_plugin" 7 | 8 | class Corona < YossarianPlugin 9 | include Cinch::Plugin 10 | use_blacklist 11 | 12 | BASE_URL = "https://corona.lmao.ninja" 13 | 14 | def usage 15 | "!corona [location] Get the current info about corona" 16 | end 17 | 18 | def match?(cmd) 19 | cmd =~ /^(!)?corona/ 20 | end 21 | 22 | match /corona$/, method: :corona_global, strip_colors: true 23 | 24 | def corona_global(m) 25 | api_endpoint = "#{BASE_URL}/v3/covid-19/all" 26 | hash = JSON.parse(URI.open(api_endpoint).read) 27 | m.reply "Global COVID-19 stats | #{Format:bold, "Active:"} #{hash["active"]} cases(#{hash["activePerOneMillion"]} per million) #{Format(:bold, "Total:")} #{hash["affectedCountries"]} Countries, #{hash["cases"]} cases, #{hash["deaths"]} deaths, #{hash["tests"]} tests(#{hash["testsPerOneMillion"]} per million). #{Format(:bold, "Today:")} #{hash["todayCases"]} cases, #{hash["todayDeaths"]} deaths.", true 28 | rescue Exception => e 29 | m.reply e.to_s, true 30 | end 31 | 32 | match /corona (.+)/, method: :corona_state, strip_colors: true 33 | 34 | def corona_state(m, state) 35 | api_endpoint = "#{BASE_URL}/v3/covid-19/states/#{URI::encode(state)}" 36 | hash = JSON.parse(URI.open(api_endpoint).read) 37 | m.reply "#{hash["state"]} COVID-19 stats | #{Format:bold, "Active:"} #{hash["active"]} cases #{Format(:bold, "Total:")} #{hash["cases"]} cases, #{hash["deaths"]} deaths, #{hash["tests"]} tests(#{hash["testsPerOneMillion"]} per million). #{Format(:bold, "Today:")} #{hash["todayCases"]} cases, #{hash["todayDeaths"]} deaths.", true 38 | 39 | rescue OpenURI::HTTPError => e 40 | corona_country(m, state) 41 | rescue Exception => e 42 | m.reply e.to_s, true 43 | end 44 | 45 | def corona_country(m, country) 46 | api_endpoint = "#{BASE_URL}/v3/covid-19/countries/#{URI::encode(country)}" 47 | hash = JSON.parse(URI.open(api_endpoint).read) 48 | m.reply "#{hash["country"]} COVID-19 stats | #{Format:bold, "Active:"} #{hash["active"]} cases(#{hash["activePerOneMillion"]} per million) #{Format(:bold, "Total:")} #{hash["cases"]} cases(#{hash["critical"]} critical), #{hash["deaths"]} deaths, #{hash["recovered"]} recovered, #{hash["tests"]} tests(#{hash["testsPerOneMillion"]} per million). #{Format(:bold, "Today:")} #{hash["todayCases"]} cases, #{hash["todayDeaths"]} deaths.", true 49 | 50 | rescue OpenURI::HTTPError => e 51 | m.reply "Nothing found for location '#{country}'.", true 52 | rescue Exception => e 53 | m.reply e.to_s, true 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /plugins/custom_triggers/custom_triggers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # custom_triggers.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that user-definable triggers for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | 13 | require_relative "../yossarian_plugin" 14 | 15 | class CustomTriggers < YossarianPlugin 16 | include Cinch::Plugin 17 | use_blacklist 18 | 19 | def initialize(*args) 20 | super 21 | @triggers_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "custom_triggers.yml")) 22 | 23 | if File.file?(@triggers_file) 24 | @triggers = YAML.load_file(@triggers_file) 25 | @triggers.default_proc = Proc.new { |h, k| h[k] = {} } 26 | else 27 | FileUtils.mkdir_p File.dirname(@triggers_file) 28 | @triggers = Hash.new { |h, k| h[k] = {} } 29 | end 30 | end 31 | 32 | def sync_triggers_file 33 | File.open(@triggers_file, "w+") do |file| 34 | file.write @triggers.to_yaml 35 | end 36 | end 37 | 38 | def usage 39 | "!trigger <command> - Manage custom triggers. Commands are add, rm, and list. Alias: !reply." 40 | end 41 | 42 | def match?(cmd) 43 | cmd =~ /^(!)?(trigger$)|(reply$)/ 44 | end 45 | 46 | match /trigger add (\S.+) -> (.+)/, method: :add_trigger # Note: mandatory " -> " string 47 | 48 | def add_trigger(m, trigger, response) 49 | channel = m.channel.to_s 50 | 51 | @triggers[channel][trigger] = response 52 | 53 | m.reply "Added trigger for \'#{trigger}\' -> \'#{response}\'.", true 54 | sync_triggers_file 55 | end 56 | 57 | match /trigger rm (\S.+)/, method: :rm_trigger 58 | 59 | def rm_trigger(m, trigger) 60 | channel = m.channel.to_s 61 | 62 | if @triggers.key?(channel) && @triggers[channel].key?(trigger) 63 | @triggers[channel].delete(trigger) 64 | m.reply "Deleted the response associated with \'#{trigger}\'.", true 65 | sync_triggers_file 66 | else 67 | m.reply "I don\'t have a response to remove for \'#{trigger}\'.", true 68 | end 69 | end 70 | 71 | match /trigger list/, method: :list_triggers 72 | 73 | def list_triggers(m) 74 | if @triggers.empty? 75 | m.reply "I don\'t currently have any triggers.", true 76 | else 77 | m.reply @triggers[m.channel.to_s].keys.join(", "), true 78 | end 79 | end 80 | 81 | listen_to :channel 82 | 83 | def listen(m) 84 | channel = m.channel.to_s 85 | 86 | if @triggers.key?(channel) && @triggers[channel].key?(m.message) 87 | m.reply "\u200B#{@triggers[channel][m.message]}" 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /plugins/github_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # github_info.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that gets information about a user or repository from GitHub. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "addressable/uri" 12 | require "json" 13 | require "open-uri" 14 | 15 | require_relative "yossarian_plugin" 16 | 17 | class GitHubInfo < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | USER_URL = "https://api.github.com/users/%{user}" 22 | REPO_URL = "https://api.github.com/repos/%{user}/%{repo}" 23 | 24 | def usage 25 | "!gh <query> - Get information about a user or repository on GitHub. Alias: !github." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?(github)|(gh)$/ 30 | end 31 | 32 | match /gh (\S+)/, method: :github_info, strip_colors: true 33 | match /github (\S+)/, method: :github_info, strip_colors: true 34 | 35 | def github_info(m, query) 36 | if query.include?("/") 37 | user, repo = query.split("/") 38 | github_repo_info(m, user, repo) 39 | else 40 | github_user_info(m, query) 41 | end 42 | end 43 | 44 | def github_user_info(m, user) 45 | url = USER_URL % { user: Addressable::URI.encode_component(user) } 46 | 47 | begin 48 | hash = JSON.parse(URI.open(url).read) 49 | hash.default = "?" 50 | 51 | if hash.key?("login") 52 | login = hash["login"] 53 | name = hash["name"] || "No name given" 54 | repos = hash["public_repos"] 55 | followers = hash["followers"] 56 | following = hash["following"] 57 | m.reply "#{login} (#{name}) has #{repos} repositories, #{followers} followers, and is following #{following} people. See more at https://github.com/#{login}", true 58 | else 59 | m.reply "No such user \'#{user}\'.", true 60 | end 61 | rescue Exception => e 62 | m.reply e.to_s, true 63 | end 64 | end 65 | 66 | def github_repo_info(m, user, repo) 67 | url = REPO_URL % { user: Addressable::URI.encode_component(user), repo: Addressable::URI.encode_component(repo) } 68 | 69 | begin 70 | hash = JSON.parse(URI.open(url).read) 71 | hash.default = "?" 72 | 73 | repo = hash["name"] 74 | desc = hash["description"] 75 | forks = hash["forks_count"] 76 | stars = hash["stargazers_count"] 77 | watchers = hash["subscribers_count"] 78 | open_issues = hash["open_issues_count"] 79 | link = hash["html_url"] 80 | 81 | m.reply "#{repo} - #{desc} (#{forks} forks, #{stars} stars, #{watchers} watchers, #{open_issues} open issues) See more at #{link}", true 82 | rescue Exception => e 83 | m.reply e.to_s, true 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /plugins/user_intros/user_intros.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # user_intros.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides custom intros for users for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | require "fileutils" 13 | 14 | require_relative "../yossarian_plugin" 15 | 16 | class UserIntros < YossarianPlugin 17 | include Cinch::Plugin 18 | use_blacklist 19 | 20 | def initialize(*args) 21 | super 22 | @intros_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "user_intros.yml")) 23 | 24 | if File.file?(@intros_file) 25 | @intros = YAML.load_file(@intros_file) 26 | else 27 | FileUtils.mkdir_p File.dirname(@intros_file) 28 | @intros = {} 29 | end 30 | end 31 | 32 | def sync_intros_file 33 | File.open(@intros_file, "w+") do |file| 34 | file.write @intros.to_yaml 35 | end 36 | end 37 | 38 | def usage 39 | "!intro <set TEXT|clear|show> - Manage the intro message for your nick." 40 | end 41 | 42 | def match?(cmd) 43 | cmd =~ /^(!)?intro$/ 44 | end 45 | 46 | match /intro (?:add|set) (.+)/, method: :set_intro 47 | 48 | def set_intro(m, intro) 49 | intro = Cinch::Formatting.unformat intro 50 | nick = m.user.nick.downcase 51 | 52 | if @intros.key?(m.channel.to_s) 53 | @intros[m.channel.to_s][nick] = intro 54 | else 55 | @intros[m.channel.to_s] = { nick => intro } 56 | end 57 | 58 | m.reply "Your intro for #{m.channel.to_s} has been set to: \'#{intro}\'.", true 59 | sync_intros_file 60 | end 61 | 62 | match /intro (clear|rm|remove|delete|del)$/, method: :remove_intro 63 | 64 | def remove_intro(m) 65 | nick = m.user.nick.downcase 66 | 67 | if @intros.key?(m.channel.to_s) && @intros[m.channel.to_s].key?(nick) 68 | @intros[m.channel.to_s].delete(nick) 69 | m.reply "Your intro for #{m.channel.to_s} has been removed.", true 70 | sync_intros_file 71 | else 72 | m.reply "You don't currently have an intro.", true 73 | end 74 | end 75 | 76 | match /intro show$/, method: :show_intro 77 | 78 | def show_intro(m) 79 | nick = m.user.nick.downcase 80 | 81 | if @intros.key?(m.channel.to_s) && @intros[m.channel.to_s].key?(nick) 82 | m.reply "Your intro is currently \'#{@intros[m.channel.to_s][nick]}\'.", true 83 | else 84 | m.reply "You don't currently have an intro.", true 85 | end 86 | end 87 | 88 | listen_to :join, method: :intro_user 89 | 90 | def intro_user(m) 91 | nick = m.user.nick.downcase 92 | 93 | return unless @intros.key?(m.channel.to_s) && @intros[m.channel.to_s].key?(nick) 94 | 95 | m.reply "\u200B#{@intros[m.channel.to_s][nick]}" 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /WRITING_PLUGINS.md: -------------------------------------------------------------------------------- 1 | PLUGIN GUIDE 2 | ============ 3 | 4 | `yossarian-bot`'s plugins are more or less ordinary 5 | [Cinch](https://github.com/cinchrb/cinch) plugins, but with a few additional 6 | tweaks. If you want to make sure that your plugin works optimally (and that 7 | I'll merge it), you should follow these guidelines: 8 | 9 | * The general outline of your plugin should look something like this: 10 | 11 | ```ruby 12 | require 'specialmodule' 13 | require 'anothermodule' 14 | 15 | require_relative './yossarian_plugin' 16 | 17 | class FooBar < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | use_auth silent: false # if you need to restrict commands to admins 21 | use_opped silent: true # if the bot needs to be an op 22 | 23 | def usage 24 | '!foo <bar> - Use foo with bar on baz.' 25 | end 26 | 27 | def match?(cmd) 28 | cmd =~ /^(!)?foo/ 29 | end 30 | 31 | match /foo (.+)/, method: :foo, strip_colors: true 32 | 33 | def foo(bar) 34 | quux = baz(bar) 35 | m.reply quux, true 36 | end 37 | end 38 | 39 | ``` 40 | 41 | * Plugin class names should be descriptive of their contents, but not overly 42 | specific or generic. Acronyms should be preserved, not lowercased. The `BTC` 43 | plugin is a good example of both of these rules - `Btc` and 44 | `BitcoinPriceFetcher` are good examples of the **wrong** thing to do. 45 | 46 | * You should **always** call `use_blacklist`. It makes sure that your plugin 47 | obeys the list of ignored nicknames/hostmasks. 48 | 49 | * You should call `use_auth` instead of reimplementing `yossarian-bot`'s 50 | authentication system if your plugin needs to be restricted to only 51 | authenticated admins. Be aware that `use_auth` restricts *all* commands in the 52 | plugin. 53 | 54 | * You should call `use_opped` if the bot needs to be a channel operator to 55 | perform an action. Be aware that `use_opped` restricts *all* commands in the 56 | plugin. 57 | 58 | * You should (re)define `usage` and `match?(cmd)` to meet your plugin's 59 | functionality and invocation, respectively. `yossarian-bot` uses these two 60 | methods to provide helpful information via `!help`. 61 | 62 | * Each `match` call should be right above the method it invokes. 63 | 64 | * If invoked methods get input from the user, remember to `Sanitize()` it or 65 | use `strip_colors: true` if that'll suffice. Web services do not generally like 66 | color codes. 67 | 68 | * When the invoked method responds to the user (and it always should), it 69 | should *generally* highlight them. In other words, highlighting the user is the 70 | correct behavior about 90% of the time. For output where a highlight would only 71 | add clutter or confusion, this can be skipped. Good exmaples of this include 72 | `UserQuotes` and `Fortune`. 73 | 74 | * Make sure any output generated is sanitized, just as it came in (unless 75 | the goal of the plugin is to add colors/codes). That means avoiding sending 76 | ASCII BELs, SOHs, or any other characters that can cause unusual or unintended 77 | behavior in either the bot or connected clients. 78 | -------------------------------------------------------------------------------- /plugins/channel_admin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # channel_admin.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin for administrating channels for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class ChannelAdmin < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | use_auth 17 | use_opped 18 | 19 | def initialize(*args) 20 | super 21 | @topic_delim = " || " 22 | end 23 | 24 | def usage 25 | "!channel <commands> - Administrate the current channel (admin required). See !help for a link to channel commands." 26 | end 27 | 28 | def match?(cmd) 29 | cmd =~ /^(!)?channel$/ 30 | end 31 | 32 | match /channel kick (\S+) (.*)/, method: :channel_kick_nick 33 | 34 | def channel_kick_nick(m, nick, reason) 35 | if m.channel.has_user?(nick) 36 | m.channel.kick nick, reason 37 | else 38 | m.reply "Nobody to kick with that nickname.", true 39 | end 40 | end 41 | 42 | match /channel ban (\S+)/, method: :channel_ban_nick 43 | 44 | def channel_ban_nick(m, nick) 45 | if m.channel.has_user?(nick) 46 | m.channel.ban User(nick) 47 | else 48 | m.channel.ban "#{nick}!*@*" 49 | end 50 | end 51 | 52 | match /channel kickban (\S+) (.*)/, method: :channel_kickban_nick 53 | 54 | def channel_kickban_nick(m, nick, reason) 55 | if m.channel.has_user?(nick) 56 | m.channel.ban User(nick) 57 | m.channel.kick nick, reason 58 | else 59 | m.channel.ban "#{nick}!*@*" 60 | end 61 | end 62 | 63 | match /channel unban (\S+)/, method: :channel_unban_nick 64 | 65 | def channel_unban_nick(m, nick) 66 | matching_masks = m.channel.bans.map { |b| b.mask }.select { |n| n.nick == nick } 67 | 68 | if matching_masks.nonempty? 69 | matching_masks.each { |mask| m.channel.unban mask } 70 | else 71 | m.reply "I couldn\'t find any bans on \'#{nick}\'.", true 72 | end 73 | end 74 | 75 | match /channel topic delim (.+)/, method: :channel_set_topic_delim 76 | 77 | def channel_set_topic_delim(m, delim) 78 | @topic_delim = " #{delim} " 79 | m.reply "Using '#{delim}' as the channel topic delimiter." 80 | end 81 | 82 | match /channel topic push (.+)/, method: :channel_push_topic 83 | 84 | def channel_push_topic(m, update) 85 | topic_arr = m.channel.topic.split(@topic_delim) 86 | topic_arr << update 87 | new_topic = topic_arr.join("#{@topic_delim}") 88 | 89 | m.channel.topic = new_topic 90 | end 91 | 92 | match /channel topic pop/, method: :channel_pop_topic 93 | 94 | def channel_pop_topic(m) 95 | topic_arr = m.channel.topic.split(@topic_delim) 96 | topic_arr.pop 97 | new_topic = topic_arr.join("#{@topic_delim}") 98 | 99 | m.channel.topic = new_topic 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /plugins/channel_moderator/channel_moderator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # channel_moderator.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that grabs moderates channels for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | 13 | require_relative "../yossarian_plugin" 14 | 15 | class ChannelModerator < YossarianPlugin 16 | include Cinch::Plugin 17 | use_auth silent: false 18 | use_opped silent: false 19 | 20 | def initialize(*args) 21 | super 22 | @rules_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "rules.yml")) 23 | 24 | if File.file?(@rules_file) 25 | @rules = @rules = YAML.load_file(@rules_file, permitted_classes: [Regexp]) 26 | @rules.default_proc = Proc.new { |h, k| h[k] = [] } 27 | else 28 | FileUtils.mkdir_p File.dirname(@rules_file) 29 | @rules = Hash.new { |h, k| h[k] = [] } 30 | end 31 | end 32 | 33 | def sync_rules_file 34 | File.open(@rules_file, "w+") do |file| 35 | file.write @rules.to_yaml 36 | end 37 | end 38 | 39 | def usage 40 | "!moderator <commands> - Configure channel moderation (admin required). See !help for a link to moderator commands." 41 | end 42 | 43 | def match?(cmd) 44 | cmd =~ /^(!)?moderator$/ 45 | end 46 | 47 | match /moderator add \/(.+)\//, method: :moderator_add_rule, strip_colors: true 48 | 49 | def moderator_add_rule(m, regex) 50 | regex = Regexp.new(regex, Regexp::IGNORECASE) 51 | 52 | @rules[m.channel.to_s] << regex 53 | sync_rules_file 54 | 55 | m.reply "Added /#{regex}/ as a rule.", true 56 | end 57 | 58 | match /moderator del \/(.+)\//, method: :moderator_del_rule, strip_colors: true 59 | 60 | def moderator_del_rule(m, regex) 61 | regex = Regexp.new(regex, Regexp::IGNORECASE) 62 | 63 | if @rules[m.channel.to_s].include?(regex) 64 | @rules[m.channel.to_s].delete(regex) 65 | sync_rules_file 66 | 67 | m.reply "Deleted /#{regex}/ from the rules.", true 68 | else 69 | m.reply "No such rule to delete.", true 70 | end 71 | end 72 | 73 | match /moderator list$/, method: :moderator_list_rules, strip_colors: true 74 | 75 | def moderator_list_rules(m) 76 | rules = @rules[m.channel.to_s].map(&:inspect).join(", ") 77 | m.reply "Current moderation expressions: #{rules}" 78 | end 79 | 80 | listen_to :channel, strip_colors: true 81 | 82 | def listen(m) 83 | channel = m.channel.to_s 84 | message = Sanitize(m.message.delete("\u200B")) 85 | 86 | if @rules.key?(channel) && Regexp.union(@rules[channel]).match(message) && !m.channel.opped?(m.user) 87 | m.channel.kick m.user, "Please follow channel rules. I\'ve sent them to you in a private message." 88 | m.user.send "Your message triggered one of these patterns:" 89 | m.user.send @rules[m.channel.to_s].map { |r| "/#{r}/" }.join(", ") 90 | m.user.send "If you think a mistake was made, please contact a channel operator." 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /plugins/user_quotes/user_quotes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # user_quotes.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides a collection of queryable quotes 7 | # for yossarian-bot. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "yaml" 13 | require "fileutils" 14 | 15 | require_relative "../yossarian_plugin" 16 | 17 | class UserQuotes < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | @@user_quote_limit = 25 22 | @@sync_interval = 10 23 | @@message_count = 0 24 | 25 | def initialize(*args) 26 | super 27 | @quotes_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "user_quotes.yml")) 28 | 29 | if File.file?(@quotes_file) 30 | @quotes = YAML.load_file(@quotes_file) 31 | else 32 | FileUtils.mkdir_p File.dirname(@quotes_file) 33 | @quotes = {} 34 | end 35 | end 36 | 37 | def sync_quotes_file 38 | File.open(@quotes_file, "w+") do |file| 39 | file.write @quotes.to_yaml 40 | end 41 | end 42 | 43 | def usage 44 | "!quote [nick] - Retrieve a random quote. If a nick is provided, a quote from that user is retrieved." 45 | end 46 | 47 | def match?(cmd) 48 | cmd =~ /^(!)?quote$/ 49 | end 50 | 51 | listen_to :channel 52 | 53 | def listen(m) 54 | chan = m.channel.to_s 55 | nick = m.user.nick 56 | 57 | if m.message !~ /^[!:.]/ 58 | @@message_count += 1 59 | 60 | if @quotes.key?(chan) 61 | if @quotes[chan].key?(nick) 62 | if @quotes[chan][nick].size < @@user_quote_limit 63 | @quotes[chan][nick] << m.message 64 | else 65 | @quotes[chan][nick].delete_at(rand(@quotes[chan][nick].length)) 66 | @quotes[chan][nick].push(m.message) 67 | end 68 | else 69 | @quotes[chan][nick] = [m.message] 70 | end 71 | else 72 | @quotes[chan] = {} 73 | end 74 | 75 | if @@message_count == @@sync_interval 76 | sync_quotes_file 77 | @@message_count = 0 78 | end 79 | end 80 | end 81 | 82 | match /quote$/, method: :random_quote 83 | 84 | def random_quote(m) 85 | chan = m.channel.to_s 86 | 87 | if chan && @quotes[chan] 88 | nick = @quotes[chan].keys.sample 89 | quote = @quotes[chan][nick].sample 90 | m.reply "\u200B#{quote} [#{nick} on #{chan}]" 91 | else 92 | m.reply "I don\'t have any quotes on this channel yet. Check back in a bit." 93 | end 94 | end 95 | 96 | match /quote (\S+)/, method: :random_quote_user 97 | 98 | def random_quote_user(m, nick) 99 | chan = m.channel.to_s 100 | 101 | if @quotes.key?(chan) && @quotes[chan].key?(nick) 102 | quote = @quotes[chan][nick].sample 103 | m.reply "\u200B#{quote} [#{nick}]" 104 | else 105 | m.reply "I don\'t have any quotes for #{nick} on this channel." 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /plugins/lennart_quotes/lennart_quotes.txt: -------------------------------------------------------------------------------- 1 | I don't usually talk about this too much, and hence I figure that people are really not aware of this, but yes, the Open Source community is full of assholes, and I probably more than most others am one of their most favourite targets. 2 | I am used to rough discussions on mailing lists, and yes, when I was younger I did not always stay technical in flamewars, but nowadays I am pretty good at that, I am sometimes articulate, but never personal. 3 | I'd actually put some blame on a certain circle of folks that play a major role in kernel development, and first and foremost Linus Torvalds himself. 4 | If Linux had success, then that certainly happened despite, not because of this behaviour. 5 | If you are a newcomer to Linux, either grow a really thick skin. Or run away, it's not a friendly place to be in. It is sad that it is that way, but it certainly is. 6 | The Linux community is dominated by western, white, straight, males in their 30s and 40s these days. I perfectly fit in that pattern, and the rubbish they pour over me is awful. I can only imagine that it is much worse for members of minorities, or people from different cultural backgrounds, in particular ones where losing face is a major issue. 7 | I get hate mail for hacking on Open Source. People have started multiple "petitions" on petition web sites, asking me to stop working (google for it). Recently, people started collecting Bitcoins to hire a hitman for me (this really happened!) 8 | Both Windows Vista and MacOS X have similar g-f logic in their audio stacks, however with PA we brought it to the next step. 9 | On Free Software systems PA doesn't really have any competitor. Some people think that JACK is one, but it actually is not. 10 | I think that PA is the way forward for audio on the Linux desktop. It may have its deficiencies -- but everything has. 11 | It always is a great feeling to see how PA got incorporated into so many distributions and how it is now used by so many people. 12 | In fact, for quite a few parts of the lower level ALSA APIs PulseAudio is the first user of all. And of course, it cannot be a surprise that we expose bugs that were previously unknown in the drivers this way. 13 | So in the past Ubuntu packaged PA in a way that, let's say, was not exactly optimal. I thought they'd gotten around fixing things since then. Turns out they didn't. 14 | Putting it all together, we realised that Upstart wouldn’t be it. 15 | So we knew that people would hate us for it. We knew we’d have to fight for a long time to get it accepted. 16 | I mean, on my laptop I even use NetworkManager, because Networkd doesn’t do wireless, right? 17 | Some people don’t realise that when Gnome started making use of Logind, I actually wrote the patch for that. 18 | So anyway, we tried to do these things in the nicest possible way, but of course people generally don’t acknowledge it! 19 | We have this speed, this quick pace with how we progress Systemd, and I think it can only work if we stay somewhat loose and not have strict regulations about how these things work. 20 | Then if you look at Gentoo, for example, they still haven’t done Systemd as default. They used to be like Arch Linux is now – they used to be the young people who adopted things quickly. But the Gentoo people aged, and they became more conservative. 21 | Nowadays Systemd is very polished in many ways, and the reason why it is so polished is because we actually listen to people. 22 | I’m in the lucky position in that there’s no pressure on me in any way. 23 | -------------------------------------------------------------------------------- /plugins/shakespearean_insults/shakespearean_insults.yml: -------------------------------------------------------------------------------- 1 | column1: 2 | - artless 3 | - bawdy 4 | - beslubbering 5 | - bootless 6 | - churlish 7 | - cockered 8 | - clouted 9 | - craven 10 | - currish 11 | - dankish 12 | - dissembling 13 | - droning 14 | - errant 15 | - fawning 16 | - fobbing 17 | - froward 18 | - frothy 19 | - gleeking 20 | - goatish 21 | - gorbellied 22 | - impertinent 23 | - infectious 24 | - jarring 25 | - loggerheaded 26 | - lumpish 27 | - mammering 28 | - mangled 29 | - mewling 30 | - paunchy 31 | - pribbling 32 | - puking 33 | - puny 34 | - qualling 35 | - rank 36 | - reeky 37 | - roguish 38 | - ruttish 39 | - saucy 40 | - spleeny 41 | - spongy 42 | - surly 43 | - tottering 44 | - unmuzzled 45 | - vain 46 | - venomed 47 | - villainous 48 | - warped 49 | - wayward 50 | - weedy 51 | - yeasty 52 | column2: 53 | - base-court 54 | - bat-fowling 55 | - beef-witted 56 | - beetle-headed 57 | - boil-brained 58 | - clapper-clawed 59 | - clay-brained 60 | - common-kissing 61 | - crook-pated 62 | - dismal-dreaming 63 | - dizzy-eyed 64 | - doghearted 65 | - dread-bolted 66 | - earth-vexing 67 | - elf-skinned 68 | - fat-kidneyed 69 | - fen-sucked 70 | - flap-mouthed 71 | - fly-bitten 72 | - folly-fallen 73 | - fool-born 74 | - full-gorged 75 | - guts-griping 76 | - half-faced 77 | - hasty-witted 78 | - hedge-born 79 | - hell-hated 80 | - idle-headed 81 | - ill-breeding 82 | - ill-nurtured 83 | - knotty-pated 84 | - milk-livered 85 | - motley-minded 86 | - onion-eyed 87 | - plume-plucked 88 | - pottle-deep 89 | - pox-marked 90 | - reeling-ripe 91 | - rough-hewn 92 | - rude-growing 93 | - rump-fed 94 | - shard-borne 95 | - sheep-biting 96 | - spur-galled 97 | - swag-bellied 98 | - tardy-gaited 99 | - tickle-brained 100 | - toad-spotted 101 | - unchin-snouted 102 | - weather-bitten 103 | column3: 104 | - apple-john 105 | - baggage 106 | - barnacle 107 | - bladder 108 | - boar-pig 109 | - bugbear 110 | - bum-bailey 111 | - canker-blossom 112 | - clack-dish 113 | - clotpole 114 | - coxcomb 115 | - codpiece 116 | - death-token 117 | - dewberry 118 | - flap-dragon 119 | - flax-wench 120 | - flirt-gill 121 | - foot-licker 122 | - fustilarian 123 | - giglet 124 | - gudgeon 125 | - haggard 126 | - harpy 127 | - hedge-pig 128 | - horn-beast 129 | - hugger-mugger 130 | - joithead 131 | - lewdster 132 | - lout 133 | - maggot-pie 134 | - malt-worm 135 | - mammet 136 | - measle 137 | - minnow 138 | - miscreant 139 | - moldwarp 140 | - mumble-news 141 | - nut-hook 142 | - pigeon-egg 143 | - pignut 144 | - puttock 145 | - pumpion 146 | - ratsbane 147 | - scut 148 | - skainsmate 149 | - strumpet 150 | - varlot 151 | - vassal 152 | - whey-face 153 | - wagtail -------------------------------------------------------------------------------- /old_plugins/now_reading/now_reading.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # now_playing.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that gets a user's current books on Goodreads. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | require "fileutils" 13 | require "goodreads" 14 | 15 | require_relative "../yossarian_plugin" 16 | 17 | class NowReading < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["GOODREADS_API_KEY"] 22 | 23 | def initialize(*args) 24 | super 25 | 26 | @username_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "goodreads_usernames.yml")) 27 | 28 | if File.file?(@username_file) 29 | @user_ids = YAML.load_file(@username_file) 30 | else 31 | FileUtils.mkdir_p File.dirname(@username_file) 32 | @user_ids = {} 33 | end 34 | 35 | @goodreads = Goodreads.new(api_key: KEY) 36 | end 37 | 38 | def sync_user_id_file 39 | File.open(@username_file, "w+") do |file| 40 | file.write @user_ids.to_yaml 41 | end 42 | end 43 | 44 | def usage 45 | "!nr [nick|link <userid>] - Get a Goodreads user's current book(s). Use link to associate <userid> with your nick. Aliases: !nowreading, !gr." 46 | end 47 | 48 | def match?(cmd) 49 | cmd =~ /^(!)?(nr$)|(nowreading$)/ 50 | end 51 | 52 | match /nowreading link (\S+)/, method: :link_account, strip_colors: true 53 | match /nr link (\S+)/, method: :link_account, strip_colors: true 54 | match /gr link (\S+)/, method: :link_account, strip_colors: true 55 | 56 | def link_account(m, user_id) 57 | if user_id =~ /\A\d+\z/ 58 | @user_ids[m.user.nick.downcase] = user_id 59 | sync_user_id_file 60 | 61 | m.reply "#{user_id} is now associated with your nick.", true 62 | else 63 | m.reply "That doesn't look like a valid Goodreads user ID.", true 64 | end 65 | end 66 | 67 | # there really ought to be a better way to do this regex 68 | match /nowreading(?:$| )(\S*)/, method: :now_reading, strip_colors: true 69 | match /nr(?:$| )(\S*)/, method: :now_reading, strip_colors: true 70 | match /gr(?:$| )(\S*)/, method: :now_reading, strip_colors: true 71 | 72 | def now_reading(m, nick) 73 | return if nick == "link" # ew 74 | 75 | nick = m.user.nick if nick.empty? 76 | 77 | user_id = @user_ids[nick.downcase] 78 | 79 | if user_id 80 | begin 81 | books = @goodreads.shelf(user_id, "currently-reading").books.map(&:book) 82 | # NOTE(ww): As of 7/2/20, this API (user.show) is returning HTTP 500s, which the Goodreads 83 | # gem then fails to handle correctly. 84 | # See https://github.com/sosedoff/goodreads/pull/60 for the Ruby-side fix. 85 | # username = @goodreads.user(user_id).user_name 86 | 87 | if books.nonempty? 88 | # 4 is a good limit 89 | nbooks = [books.size, 4].min 90 | 91 | reading = nbooks.times.map do |i| 92 | authors = books[i].authors.map { |_, author| author.name }.join ", " 93 | "#{books[i].title} (#{authors})" 94 | end.join ", " 95 | 96 | m.reply "Currently reading: #{reading}.", true 97 | else 98 | m.reply "#{nick} (#{user_id}) isn't currently reading anything.", true 99 | end 100 | rescue Goodreads::NotFound => e 101 | m.reply "I couldn't find a Goodreads user with ID: #{user_id}.", true 102 | rescue Exception => e 103 | m.reply e.to_s, true 104 | raise e 105 | end 106 | else 107 | m.reply "I don\'t have a Goodreads account associated with #{nick}. Add one with !nr link <userid>.", true 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (5.2.8) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 0.7, < 2) 7 | minitest (~> 5.1) 8 | tzinfo (~> 1.1) 9 | addressable (2.8.7) 10 | public_suffix (>= 2.0.2, < 7.0) 11 | base64 (0.2.0) 12 | concurrent-ruby (1.1.10) 13 | connection_pool (2.4.1) 14 | crass (1.0.6) 15 | daemons (1.4.1) 16 | domain_name (0.6.20240107) 17 | duck-duck-go (1.1.3) 18 | httpclient 19 | json 20 | faraday (0.17.6) 21 | multipart-post (>= 1.2, < 3) 22 | flippy (0.1.3) 23 | optimist 24 | ruby-termios 25 | genregen (0.0.9) 26 | google-search (1.0.3) 27 | json 28 | grinch (1.1.0) 29 | grinch-identify (1.8.0) 30 | grinch (~> 1.1) 31 | haste (0.3.0) 32 | faraday (~> 0.9) 33 | json 34 | htmlentities (4.3.4) 35 | http-cookie (1.0.8) 36 | domain_name (~> 0.5) 37 | httparty (0.21.0) 38 | mini_mime (>= 1.0.0) 39 | multi_xml (>= 0.5.2) 40 | httpclient (2.8.3) 41 | i18n (1.10.0) 42 | concurrent-ruby (~> 1.0) 43 | json (2.10.2) 44 | lastfm (1.27.4) 45 | httparty 46 | xml-simple 47 | leetspeak (0.1.1) 48 | logger (1.6.4) 49 | mechanize (2.14.0) 50 | addressable (~> 2.8) 51 | base64 52 | domain_name (~> 0.5, >= 0.5.20190701) 53 | http-cookie (~> 1.0, >= 1.0.3) 54 | mime-types (~> 3.3) 55 | net-http-digest_auth (~> 1.4, >= 1.4.1) 56 | net-http-persistent (>= 2.5.2, < 5.0.dev) 57 | nkf 58 | nokogiri (~> 1.11, >= 1.11.2) 59 | rubyntlm (~> 0.6, >= 0.6.3) 60 | webrick (~> 1.7) 61 | webrobots (~> 0.1.2) 62 | mime-types (3.6.0) 63 | logger 64 | mime-types-data (~> 3.2015) 65 | mime-types-data (3.2024.1203) 66 | mini_mime (1.1.2) 67 | mini_portile2 (2.8.8) 68 | minitest (5.16.0) 69 | multi_xml (0.6.0) 70 | multipart-post (2.3.0) 71 | myanimelist (1.0.0) 72 | rest-client (= 2.0.2) 73 | xml-simple (= 1.1.5) 74 | net-http-digest_auth (1.4.1) 75 | net-http-persistent (4.0.5) 76 | connection_pool (~> 2.2) 77 | netrc (0.11.0) 78 | nkf (0.2.0) 79 | nokogiri (1.18.3) 80 | mini_portile2 (~> 2.8.2) 81 | racc (~> 1.4) 82 | open_uri_redirections (0.2.1) 83 | optimist (3.0.1) 84 | public_suffix (6.0.1) 85 | racc (1.8.1) 86 | research_topicgen (0.1.6) 87 | rest-client (2.0.2) 88 | http-cookie (>= 1.0.2, < 2.0) 89 | mime-types (>= 1.16, < 4.0) 90 | netrc (~> 0.8) 91 | rexml (3.4.1) 92 | ruby-eval-in (0.0.8) 93 | nokogiri 94 | ruby-termios (1.1.0) 95 | ruby-upworthy (1.0.1) 96 | rubyntlm (0.6.5) 97 | base64 98 | sanitize (7.0.0) 99 | crass (~> 1.0.2) 100 | nokogiri (>= 1.16.8) 101 | telegraph (1.0.3) 102 | thread_safe (0.3.6) 103 | time_difference (0.7.0) 104 | activesupport (~> 5.1) 105 | tzinfo (1.2.10) 106 | thread_safe (~> 0.1) 107 | webrick (1.9.1) 108 | webrobots (0.1.2) 109 | wolfram (0.3.0) 110 | nokogiri (~> 1, >= 1.4.3) 111 | xkcd (1.1.1) 112 | google-search 113 | nokogiri (>= 1.5.0) 114 | xml-simple (1.1.5) 115 | 116 | PLATFORMS 117 | ruby 118 | 119 | DEPENDENCIES 120 | addressable 121 | daemons 122 | duck-duck-go 123 | flippy 124 | genregen 125 | grinch (~> 1.1) 126 | grinch-identify (~> 1.8) 127 | haste 128 | htmlentities 129 | json 130 | lastfm 131 | leetspeak 132 | mechanize 133 | myanimelist (~> 1.0) 134 | nokogiri 135 | open_uri_redirections 136 | research_topicgen 137 | rexml 138 | ruby-eval-in 139 | ruby-upworthy 140 | sanitize (~> 7.0.0) 141 | telegraph 142 | time_difference 143 | wolfram (~> 0.3) 144 | xkcd 145 | 146 | BUNDLED WITH 147 | 2.1.4 148 | -------------------------------------------------------------------------------- /plugins/now_playing/now_playing.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # now_playing.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that gets a user's last played track from Last.fm. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require "yaml" 12 | require "fileutils" 13 | require "lastfm" 14 | 15 | require_relative "../yossarian_plugin" 16 | 17 | class NowPlaying < YossarianPlugin 18 | include Cinch::Plugin 19 | use_blacklist 20 | 21 | KEY = ENV["LASTFM_API_KEY"] 22 | SECRET = ENV["LASTFM_API_SECRET"] 23 | 24 | def initialize(*args) 25 | super 26 | 27 | @username_file = File.expand_path(File.join(File.dirname(__FILE__), @bot.server_id, "lastfm_usernames.yml")) 28 | 29 | if File.file?(@username_file) 30 | @usernames = YAML.load_file(@username_file) 31 | else 32 | FileUtils.mkdir_p File.dirname(@username_file) 33 | @usernames = {} 34 | end 35 | 36 | @lastfm = Lastfm.new(KEY, SECRET) if KEY && SECRET 37 | end 38 | 39 | def sync_username_file 40 | File.open(@username_file, "w+") do |file| 41 | file.write @usernames.to_yaml 42 | end 43 | end 44 | 45 | def usage 46 | "!np [nick|link <username>] - Get a Last.fm user's last played track. Use link to associate <username> with your nick. Alias: !nowplaying." 47 | end 48 | 49 | def match?(cmd) 50 | cmd =~ /^(!)?(np$)|(nowplaying$)/ 51 | end 52 | 53 | match /nowplaying link (\S+)/, method: :link_account, strip_colors: true 54 | match /np link (\S+)/, method: :link_account, strip_colors: true 55 | 56 | def link_account(m, username) 57 | @usernames[m.user.nick.downcase] = username 58 | sync_username_file 59 | 60 | m.reply "#{username} is now associated with your nick.", true 61 | end 62 | 63 | # there really ought to be a better way to do this regex 64 | match /nowplaying(?:$| )(\S*)/, method: :now_playing, strip_colors: true 65 | match /np(?:$| )(\S*)/, method: :now_playing, strip_colors: true 66 | 67 | def now_playing(m, nick) 68 | return if nick == "link" # ew 69 | 70 | if @lastfm 71 | nick = m.user.nick if nick.empty? 72 | 73 | username = @usernames[nick.downcase] 74 | 75 | if username 76 | begin 77 | # TODO: Retrieve more recent tracks here, and filter for the one closest to the current 78 | # time. Ideally we wouldn't do something so stupid, but Last.fm's API doesn't appear 79 | # to respect its own `from` and `to` parameters. 80 | info = @lastfm.user.get_recent_tracks(user: username, limit: 1) 81 | 82 | if info 83 | # APIs should always return a uniform type... 84 | info = info.first if info.is_a?(Array) 85 | 86 | active = if info["nowplaying"] 87 | "is now playing" 88 | else 89 | "last played" 90 | end 91 | 92 | artist = info["artist"]["content"] 93 | song = info["name"] 94 | album = info["album"]["content"] 95 | 96 | if album 97 | m.reply "#{username} #{active} \"#{song}\" by #{artist} on #{album}.", true 98 | else 99 | m.reply "#{username} #{active} \"#{song}\" by #{artist}.", true 100 | end 101 | else 102 | m.reply "#{username} has no scrobbles.", true 103 | end 104 | rescue Exception => e 105 | m.reply e.to_s.strip, true 106 | raise e 107 | end 108 | else 109 | m.reply "I don\'t have a last.fm account associated with #{nick}. Add one with !np link <username>.", true 110 | end 111 | else 112 | m.reply "#{self.class.name}: Internal error (missing API key(s))." 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /yossarian-bot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # -*- coding: utf-8 -*- 4 | # yossarian-bot.rb 5 | # Author: William Woodruff 6 | # ------------------------ 7 | # A call-and-response IRC bot for entertainment. 8 | # ------------------------ 9 | # This code is licensed by William Woodruff under the MIT License. 10 | # http://opensource.org/licenses/MIT 11 | 12 | require "set" 13 | require "cinch" 14 | require "cinch/plugins/identify" 15 | require "yaml" 16 | 17 | config_file = File.expand_path(File.join(File.dirname(__FILE__), "config.yml")) 18 | version_file = File.expand_path(File.join(File.dirname(__FILE__), "version.yml")) 19 | plugins_file = File.expand_path(File.join(File.dirname(__FILE__), "plugins.yml")) 20 | 21 | if File.file?(config_file) && File.file?(version_file) && File.file?(plugins_file) 22 | config = YAML.load_file(config_file) 23 | version = YAML.load_file(version_file) 24 | plugins = YAML.load_file(plugins_file)["plugins"] 25 | else 26 | abort("Fatal: Missing one of: config.yml, version.yml, plugins.yml.") 27 | end 28 | 29 | # Update the environment before we load extensions and plugins. 30 | ENV.update(config["environment"] || {}) 31 | 32 | Dir[File.dirname(__FILE__) + "/extend/**/*.rb"].each do |extension| 33 | require extension 34 | end 35 | 36 | Dir[File.dirname(__FILE__) + "/plugins/**/*.rb"].each do |plugin| 37 | require plugin 38 | end 39 | 40 | server_threads = [] 41 | 42 | # rubocop:disable Metrics/BlockLength 43 | config["servers"].each do |server_name, server_info| 44 | server_threads << Thread.new do 45 | Cinch::Bot.new do 46 | @starttime = Time.now 47 | @version = version 48 | @admins = server_info["admins"] || [] 49 | @blacklist = server_info["blacklist"]&.to_set || Set.new 50 | @all_plugins = plugins.map do |plugin| 51 | Object.const_get(plugin) 52 | end 53 | @server_id = server_info["server_id"] || server_name 54 | 55 | # rubocop:disable Style/TrivialAccessors 56 | def starttime 57 | @starttime 58 | end 59 | 60 | def version 61 | @version 62 | end 63 | 64 | def admins 65 | @admins 66 | end 67 | 68 | def blacklist 69 | @blacklist 70 | end 71 | 72 | def all_plugins 73 | @all_plugins 74 | end 75 | 76 | def server_id 77 | @server_id 78 | end 79 | # rubocop:enable Style/TrivialAccessors 80 | 81 | configure do |conf| 82 | conf.nick = server_info["nick"] || "yossarian-bot" 83 | conf.realname = "yossarian-bot" 84 | conf.user = "yossarian-bot" 85 | conf.password = server_info["password"] 86 | conf.max_messages = 1 87 | conf.server = server_name 88 | conf.channels = server_info["channels"] 89 | conf.port = server_info["port"] || 6667 90 | conf.ssl.use = server_info["ssl"] || false 91 | conf.plugins.prefix = Regexp.new(server_info["prefix"] || /^!/) 92 | conf.plugins.plugins = @all_plugins.dup 93 | conf.plugins.plugins << Cinch::Plugins::Identify 94 | 95 | if server_info.key? "sasl" 96 | conf.sasl.username = server_info["sasl"]["password"] || conf.nick 97 | conf.sasl.password = server_info["sasl"]["password"] 98 | end 99 | 100 | if server_info.key?("auth") 101 | conf.plugins.options[Cinch::Plugins::Identify] = { 102 | type: server_info["auth"]["type"].to_sym, 103 | password: server_info["auth"]["password"], 104 | } 105 | end 106 | 107 | if server_info.key?("disabled_plugins") 108 | server_info["disabled_plugins"].each do |plugin| 109 | conf.plugins.plugins.delete(Object.const_get(plugin)) 110 | end 111 | end 112 | end 113 | end.start 114 | end 115 | end 116 | # rubocop:enable Metrics/BlockLength 117 | 118 | server_threads.each(&:join) 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yossarian-bot 2 | ============= 3 | 4 | ![license](https://raster.shields.io/badge/license-MIT%20with%20restrictions-green.png) 5 | 6 | An entertaining IRC bot that's easy to extend. 7 | 8 | ## Features: 9 | * Simple real-time administration. 10 | * Unix fortunes (`fortune` must be present) 11 | * Catch-22 quotes 12 | * UrbanDictionary queries 13 | * Wolfram|Alpha queries 14 | * Smart weather queries (Wunderground) 15 | * Google searches 16 | * YouTube searches 17 | * ROT13 message "encryption" 18 | * Magic 8 Ball queries 19 | * Dictionary queries (Merriam-Webster) 20 | * Cleverbot discussions 21 | * Channel 'seen' log 22 | * Link compression (TinyURL) 23 | * ...and [much more](COMMANDS.md)! 24 | 25 | ## Installation 26 | 27 | First, clone the repo and install `yossarian-bot`'s dependencies: 28 | 29 | ```bash 30 | $ git clone https://github.com/woodruffw/yossarian-bot 31 | $ cd yossarian-bot 32 | $ bundle install 33 | ``` 34 | 35 | If you get errors during the bundle installation process, make sure that you're 36 | using Ruby 3.0 or later and have Ruby's development headers installed. You may need them 37 | from your package manager. Earlier versions of Ruby *might* work, but are 38 | not guaranteed or tested. 39 | 40 | `yossarian-bot` also requires API keys for several services. Make sure that 41 | they are exported to the environment (or set in the configuration) as follows: 42 | 43 | * Wolfram|Alpha - `WOLFRAM_ALPHA_APPID_KEY` 44 | * Weather Underground - `WUNDERGROUND_API_KEY` 45 | * WeatherStack - `WEATHERSTACK_API_KEY` 46 | * Merriam-Webster - `MERRIAM_WEBSTER_API_KEY` 47 | * YouTube (v3) - `YOUTUBE_API_KEY` 48 | * Last.fm - `LASTFM_API_KEY`, `LASTFM_API_SECRET` 49 | * Open Exchange Rates - `OEX_API_KEY` 50 | * Giphy - `GIPHY_API_KEY` 51 | * BreweryDB - `BREWERYDB_API_KEY` 52 | * AirQuality - `AIRNOW_API_KEY` 53 | * OMDB - `OMDB_API_KEY` 54 | 55 | Additionally, the `fortune` utility must be present in order for Unix fortunes 56 | to work correctly. Some package managers also provide the `fortunes`, 57 | `fortunes-off`, and `fortunes-bofh-excuses` packages for additional fortune 58 | messages. 59 | 60 | ### Running 61 | 62 | Once all dependencies are installed, `yossarian-bot` can be run as follows: 63 | 64 | ```bash 65 | $ ruby bot-control.rb start 66 | $ # OR: 67 | $ ruby yossarian-bot.rb # not run in background 68 | ``` 69 | 70 | ### Using Docker 71 | 72 | ```bash 73 | docker build -t yossarian-bot:latest . 74 | docker run -v $PWD/config.yml:/config.yml yossarian-bot 75 | ``` 76 | 77 | ## Using the bot 78 | 79 | ### Configuration Options 80 | 81 | `yossarian-bot` is configured via a YAML file named *config.yml*. 82 | 83 | Look at [the example config.yml](config.yml.example) to see a list of optional 84 | and required keys. 85 | 86 | ### Commands 87 | 88 | There are a bunch of commands that `yossarian-bot` accepts. You can 89 | see a complete list in the [COMMANDS](COMMANDS.md) file. 90 | 91 | ### Matches 92 | 93 | `yossarian-bot` matches all HTTP(S) links and messages the title of the linked 94 | HTML page. This feature can be disabled by adding `LinkTitling` to the server's 95 | `disabled_plugins` array in `config.yml`. 96 | 97 | Messages of the form `s/(.+)/(.+)` are also matched, and the first pattern 98 | matched is applied to the user's last previous message, with the second match 99 | replacing it. For example, a typo like "this is a setnence" can be corrected 100 | with `s/setnence/sentence`. This feature can be disabled by adding 101 | `RegexReplace` to the server's `disabled_plugins` array in `config.yml`. 102 | 103 | ## Contributing 104 | 105 | Contributions to `yossarian-bot` are welcomed and appreciated. 106 | 107 | If you're writing a plugin, check out the 108 | [quick style guide](WRITING_PLUGINS.md) to writing plugins for `yossarian-bot`. 109 | 110 | If you'd like to contribute but don't have any contributions in mind, check out 111 | the [open issues](https://github.com/woodruffw/yossarian-bot/issues?q=is%3Aopen+is%3Aissue). 112 | They're regularly updated with things that can be fixed, improved, and added. 113 | 114 | ## License 115 | 116 | `yossarian-bot` is licensed under a restricted modification of the MIT license. 117 | 118 | For the exact terms, see the [license](LICENSE) file. 119 | -------------------------------------------------------------------------------- /plugins/catch22.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # catch22.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin that provides random Catch-22 quotes for yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class Catch22 < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | 17 | QUOTES = [ 18 | "He was going to live forever, or die in the attempt.", 19 | "Just because you're paranoid doesn't mean they aren't after you.", 20 | "Anything worth dying for is certainly worth living for.", 21 | "Insanity is contagious.", 22 | "It doesn't make a damned bit of difference who wins the war to someone who's dead.", 23 | "It doesn't make sense. It isn't even good grammar.", 24 | "The country was in peril; he was jeopardizing his traditional rights of freedom and independence by daring to exercise them.", 25 | "I want to keep my dreams, even bad ones, because without them, I might have nothing all night long.", 26 | "The Texan turned out to be good-natured, generous and likable. In three days no one could stand him.", 27 | "Some men are born mediocre, some men achieve mediocrity, and some men have mediocrity thrust upon them.", 28 | "There is no disappointment so numbing...as someone no better than you achieving more.", 29 | "Destiny is a good thing to accept when it's going your way. When it isn't, don't call it destiny; call it injustice, treachery, or simple bad luck.", 30 | "Well, he died. You don't get any older than that.", 31 | "\"Consciously, sir, consciously,\" Yossarian corrected in an effort to help. \"I hate them consciously.\"", 32 | "Prostitution gives her an opportunity to meet people. It provides fresh air and wholesome exercise, and it keeps her out of trouble.", 33 | "\"What would they do to me,\" he asked in confidential tones, \"if I refused to fly them?\"", 34 | "\"If you're going to be shot, whose side do you expect me to be on?\" ex-P.F.C. Wintergreen retorted.", 35 | "\"Because...\" Clevinger sputtered, and turned speechless with frustration. Clevinger really thought he was right, but Yossarian had proof, because strangers he didn't know shot at him with cannons every time he flew up into the air to drop bombs on them, and it wasn't funny at all.", 36 | "Catch-22 did not exist, he was positive of that, but it made no difference.", 37 | "He was a self-made man who owed his lack of success to nobody.", 38 | "Major Major\'s elders disliked him because he was such a flagrant nonconformist.", 39 | "You have no respect for excessive authority or obsolete traditions. You're dangerous and depraved, and you ought to be taken outside and shot!", 40 | "Clevinger had a mind, and Lieutenant Scheisskoph had noticed that people with minds tended to get pretty smart at times.", 41 | "When I grow up I want to be a little boy.", 42 | "He woke up blinking with a slight pain in his head and opened his eyes upon a world boiling in chaos in which everything was in proper order.", 43 | "I\'m not running away from my responsibilities. I'm running to them. There\'s nothing negative about running away to save my life.", 44 | "That\'s some catch, that Catch-22,\" he observed. \"It\'s the best there is,\" Doc Daneeka agreed.", 45 | "No, no darling. Because you\'re crazy.", 46 | "When I was a kid, I used to walk around all day with crab apples in my cheeks. One in each cheek.", 47 | "Catch-22 says they have the right to do anything we can't stop them from doing.", 48 | "There was one catch, and that was Catch-22.", 49 | "He was a militant idealist who crusaded against racial bigotry by growing faint in its presence.", 50 | "Aarfy was a dedicated fraternity man who loved cheerleading and class reunions and did not have brains enough to be afraid.", 51 | "Any fool can make money these days and most of them do. But what about people with talent and brains? Name, for example, one poet who makes money.", 52 | ] 53 | 54 | def usage 55 | "!c22 - Get a random Catch-22 quote. Alias: !catch22" 56 | end 57 | 58 | def match?(cmd) 59 | cmd =~ /^(!)?(catch22)|(c22)$/ 60 | end 61 | 62 | match /c22$/, method: :catch22 63 | match /catch22$/, method: :catch22 64 | 65 | def catch22(m) 66 | m.reply QUOTES.sample, true 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /plugins/fieri_quotes/fieri_quotes.txt: -------------------------------------------------------------------------------- 1 | Holy moly, stromboli! 2 | I’m driving the bus to flavor town. 3 | They make a porchetta you won’t forgetta. 4 | It was a lightning bolt of an idea in flavor town that pranked the unprankable mayor, Guy Fieri. 5 | I could put this on a flip flop and it would taste good. 6 | Did you get any tater with that gator? 7 | This sauce is money! 8 | You’re takin’ the gobble full throttle! 9 | This place is bananas. And bananas is good. 10 | Love, peace, and taco grease! 11 | His seafood is so fresh it’ll slap ya. 12 | You don’t have to eat a whole cheeseburger, just take a piece of the cheeseburger. 13 | Some people are just born to cook and talk. 14 | When cooking for a big crew of hungry dudes who’ve been sleeping in a parking lot, do not think you can get away with fettucini Alfredo. 15 | No matter how tough the meat may be, it's going to be tender if you slice it thin enough. 16 | Don’t ever use lighter fluid. It’s un-American. Amateurs, losers, and idiots use lighter fluid. 17 | I wanna be the ambassador to Chimichanga Flavour Town. 18 | I'm a five-seasons griller... I don't care what the weather is like. My hair is impervious to any kind of dampness, so I don't have too much to worry about. 19 | Fried green tomatoes, brother that’s a symposium of flavor. 20 | Splash some rub around the rest of the hog for good measure. This really doesn’t do a dang thing, but it makes you feel good about things and makes for good drama. 21 | Shut the front door, son of Tatum O’Neal, that’s dynamite. 22 | On the final leg of the Kid Rock culinary cruise, we ended up at the brewery where Kid Rock made Badass Beer. Now if you’re going to call it Badass beer it better be badass, and all I can tell you is the name fits the bill. Just like his music, the dude delivers. Not one to stray far from his roots or waver on his stance to do good for the city of Detroit, when Kid Rock’s Badass producer unexpectedly closed shop in 2012, Rock knew what to do: build a world-class brewery in the heart of the city staffed by Detroiters and supporting Detroit. Opening Summer 2013. Badass Beer: Trouble Has Been Brewed. 23 | Chef Matt says, ‘Get jiggy with it, have some fun! 24 | People who like haggis call it spicy, creamy, rich, and buttery — I don’t wanna tell you what I call it… ha ha. 25 | My favorite line: ‘Do you get any tater with that gator? James Spader likes gator.’ (Ha ha ha, I kill me.) 26 | I don’t know if it’s fair to call their Russian dressing Russian dressing — it should be called something sexy, like liquid Moscow. 27 | I lay claim to the knuckle sandwich… it’s my brand, my logo, hell, even my tattoo, so when I find out that two dudes in Austin have opened up a sandwich joint and one of their menu items is the knuckle sandwich, I tell you what, they’d better deliver the real deal. (jk.) 28 | I sucked at making my Yorkshire pudding before getting schooled by Anne. Now they’re puffy McMagic, not flat McTragic.)… She could feed me beef six ways to Sunday. 29 | Holy clam, Batman! 30 | The sauce is money! 31 | How money is that? 32 | I’m mining for food in Flavortown River. 33 | This patio goes off the hook - I think the folks are in a Flavortown food coma. 34 | You can find that dictionary at the Flavortown Library. 35 | What a hot frisbee of fun! 36 | This, my friend, is bordering on illegal. And I’m not talking about an ill eagle. 37 | Do you get any tater with that gator? 38 | It’s like a hot-dog lasso on the ranch in Flavortown. 39 | That’s in the tank that fuels the bus that goes to Flavortown. 40 | Chef Matt says, ‘Get jiggy with it, have some fun! 41 | Dude, I’ve been stricken by chicken! 42 | Do you have a garden hose? Something I can clean up with? 43 | I feel like I’m in lasagna surgery here at Flavortown Memorial. 44 | This is Gangsta! 45 | This is Money! 46 | We’re Riding the Bus to Flavortown! 47 | This is Off the Hook! 48 | This is Out of Bounds! 49 | Shut the Front Door. 50 | If you slow it down, eat in courses, your body, mind, stomach will catch up with this full feeling and you won't eat as much. 51 | Lots of people make fun of me, but the truth is I'm just a man. I like food, I like people, and I like making people happy with food. I have a wife; I have two sons. I love them more than anything. Sure, my TV personality might not be for everyone, but that's okay. I just want to live my life. Please, leave me in peace. I am a man. I have dignity. I am a man. 52 | I love watching, I love getting all the science about food. That's one of my favorite things. 53 | Some people are just born to cook and talk. 54 | Liver is my number one most hated food. Oh, God, I get sick talking about it! 55 | Capital T tasty. 56 | Pig candy. 57 | Holy clam, Batman! 58 | You’re takin’ the gobble full throttle! 59 | Like a speed-bump in Flavortown. 60 | -------------------------------------------------------------------------------- /plugins/bot_admin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # bot_admin.rb 4 | # Author: William Woodruff 5 | # ------------------------ 6 | # A Cinch plugin for administrating yossarian-bot. 7 | # ------------------------ 8 | # This code is licensed by William Woodruff under the MIT License. 9 | # http://opensource.org/licenses/MIT 10 | 11 | require_relative "yossarian_plugin" 12 | 13 | class BotAdmin < YossarianPlugin 14 | include Cinch::Plugin 15 | use_blacklist 16 | use_auth 17 | 18 | def usage 19 | "!admin <commands> - Administrate the bot. See !help for a link to admin commands." 20 | end 21 | 22 | def match?(cmd) 23 | cmd =~ /^(!)?admin$/ 24 | end 25 | 26 | match /admin plugin list/, method: :plugin_list 27 | 28 | def plugin_list(m) 29 | all_plugin_names = @bot.all_plugins.map(&:name) 30 | active_plugin_names = @bot.plugins.map(&:class).map(&:name) 31 | 32 | plugins = all_plugin_names.map do |pn| 33 | Format(active_plugin_names.include?(pn) ? :green : :red, pn) 34 | end.join(", ") 35 | 36 | # temporarily allow up to three messages due to long plugin lists 37 | @bot.config.max_messages = 3 38 | m.reply "Available plugins: #{plugins}", true 39 | @bot.config.max_messages = 1 40 | end 41 | 42 | 43 | match /admin plugin enable (\w+)/, method: :plugin_enable 44 | 45 | def plugin_enable(m, name) 46 | active_plugin_names = @bot.plugins.map(&:class).map(&:name) 47 | 48 | @bot.all_plugins.each do |plugin| 49 | if plugin.name == name && active_plugin_names.exclude?(plugin.name) 50 | @bot.plugins.register_plugin(plugin) 51 | m.reply "#{name} has been enabled.", true 52 | return 53 | end 54 | end 55 | 56 | m.reply "#{name} is already enabled or does not exist.", true 57 | end 58 | 59 | match /admin plugin disable (\w+)/, method: :plugin_disable 60 | 61 | def plugin_disable(m, name) 62 | @bot.plugins.each do |plugin| 63 | if plugin.class.name == name 64 | @bot.plugins.unregister_plugin(plugin) 65 | m.reply "#{name} has been disabled.", true 66 | return 67 | end 68 | end 69 | 70 | m.reply "#{name} is already disabled or does not exist.", true 71 | end 72 | 73 | match /admin quit/, method: :bot_quit 74 | 75 | def bot_quit(m) 76 | m.reply "Goodbye!" 77 | @bot.quit 78 | end 79 | 80 | match /admin auth (\S+)/, method: :bot_add_admin 81 | 82 | def bot_add_admin(m, nick) 83 | if @bot.admins.exclude?(nick) 84 | @bot.admins << nick 85 | m.reply "Added #{nick} as an admin.", true 86 | else 87 | m.reply "#{nick} is already an admin.", true 88 | end 89 | end 90 | 91 | match /admin deauth (\S+)/, method: :bot_remove_admin 92 | 93 | def bot_remove_admin(m, nick) 94 | if @bot.admins.include?(nick) 95 | @bot.admins.delete(nick) 96 | m.reply "#{nick} is no longer an admin.", true 97 | else 98 | m.reply "No admin \'#{nick}\' to remove.", true 99 | end 100 | end 101 | 102 | match /admin join (\S+)/, method: :bot_join_channel 103 | 104 | def bot_join_channel(m, chan) 105 | if @bot.channels.exclude?(chan) 106 | @bot.join(chan) 107 | m.reply "I\'ve joined #{chan}.", true 108 | else 109 | m.reply "I\'m already in #{chan}!", true 110 | end 111 | end 112 | 113 | match /admin leave (\S+)/, method: :bot_leave_channel 114 | 115 | def bot_leave_channel(m, chan) 116 | if @bot.channels.include?(chan) 117 | m.reply "I\'m leaving #{chan}.", true 118 | @bot.part(chan) 119 | else 120 | m.reply "I\'m not in that channel.", true 121 | end 122 | end 123 | 124 | match /admin cycle (\S+)/, method: :bot_cycle_channel 125 | 126 | def bot_cycle_channel(m, chan) 127 | if @bot.channels.include?(chan) 128 | m.reply "I\'m cycling on #{chan}.", true 129 | @bot.part(chan) 130 | @bot.join(chan) 131 | else 132 | m.reply "I\'m not in that channel.", true 133 | end 134 | end 135 | 136 | match /admin ignore nick (\S+)/, method: :bot_ignore_nick 137 | 138 | def bot_ignore_nick(m, nick) 139 | host = User(nick).host 140 | 141 | @bot.blacklist << nick 142 | 143 | if host && !host.empty? 144 | @bot.blacklist << host 145 | m.reply "Ignoring everything from #{host} (#{nick}).", true 146 | else 147 | m.reply "Ignoring everything from #{nick}.", true 148 | end 149 | end 150 | 151 | match /admin ignore host (\S+)/, method: :bot_ignore_host 152 | 153 | def bot_ignore_host(m, host) 154 | @bot.blacklist << host 155 | 156 | m.reply "Ignoring everything from #{host}.", true 157 | end 158 | 159 | match /admin unignore nick (\S+)/, method: :bot_unignore_nick 160 | 161 | def bot_unignore_nick(m, nick) 162 | host = User(nick).host 163 | 164 | @bot.blacklist.delete(nick) 165 | 166 | @bot.blacklist.delete(host) unless host.empty? 167 | 168 | m.reply "Removed any records associated with #{nick} from the blacklist.", true 169 | end 170 | 171 | match /admin unignore host (\S+)/, method: :bot_unignore_host 172 | 173 | def bot_unignore_host(m, host) 174 | @bot.blacklist.delete(host) 175 | 176 | m.reply "Removed #{host} from the blacklist.", true 177 | end 178 | 179 | match /admin nick (\S+)/, method: :bot_nick 180 | 181 | def bot_nick(m, nick) 182 | m.reply "Changing my nickname to #{nick}.", true 183 | @bot.nick = nick 184 | end 185 | 186 | match /admin say (#\S+) (.+)/, method: :bot_say 187 | 188 | def bot_say(_m, chan, msg) 189 | Channel(chan).send msg 190 | end 191 | 192 | match /admin act (#\S+) (.+)/, method: :bot_act 193 | 194 | def bot_act(_m, chan, msg) 195 | Channel(chan).action msg 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /plugins/rms_quotes/rms_quotes.txt: -------------------------------------------------------------------------------- 1 | Sharing is good, and with digital technology, sharing is easy. 2 | If you want to accomplish something in the world, idealism is not enough - you need to choose a method that works to achieve the goal. 3 | Proprietary software tends to have malicious features. The point is with a proprietary program, when the users don't have the source code, we can never tell. So you must consider every proprietary program as potential malware. 4 | Facebook is not your friend, it is a surveillance engine. 5 | I suppose many people will continue moving towards careless computing, because there's a sucker born every minute. 6 | Free software is software that respects your freedom and the social solidarity of your community. So it's free as in freedom. 7 | Android is very different from the GNU/Linux operating system because it contains very little of GNU. Indeed, just about the only component in common between Android and GNU/Linux is Linux, the kernel. 8 | With paper printed books, you have certain freedoms. You can acquire the book anonymously by paying cash, which is the way I always buy books. I never use a credit card. I don't identify to any database when I buy books. Amazon takes away that freedom. 9 | CD stores have the disadvantage of an expensive inventory, but digital bookshops would need no such thing: they could write copies at the time of sale on to memory sticks, and sell you one if you forgot your own. 10 | The computer industry is the only industry that is more fashion-driven than women's fashion. 11 | In essence, Chrome OS is the GNU/Linux operating system. However, it is delivered without the usual applications, and rigged up to impede and discourage installing applications. 12 | The idea of copyright did not exist in ancient times, when authors frequently copied other authors at length in works of non-fiction. This practice was useful, and is the only way many authors' works have survived even in part. 13 | Fighting patents one by one will never eliminate the danger of software patents, any more than swatting mosquitoes will eliminate malaria. 14 | Anything that prevents you from being friendly, a good neighbour, is a terror tactic. 15 | People sometimes ask me if it is a sin in the Church of Emacs to use vi. Using a free version of vi is not a sin; it is a penance. So happy hacking. 16 | Value your freedom or you will lose it, teaches history. 'Don't bother us with politics', respond those who don't want to learn. 17 | All governments should be pressured to correct their abuses of human rights. 18 | Whether gods exist or not, there is no way to get absolute certainty about ethics. Without absolute certainty, what do we do? We do the best we can. 19 | I could have made money this way, and perhaps amused myself writing code. But I knew that at the end of my career, I would look back on years of building walls to divide people, and feel I had spent my life making the world a worse place. 20 | If you use a proprietary program or somebody else's web server, you're defenceless. You're putty in the hands of whoever developed that software. 21 | Android is a major step towards an ethical, user-controlled, free-software portable phone, but there is a long way to go. 22 | Officially, MPAA stands for Motion Picture Association of America, but I suggest that MPAA stands for Malicious Power Attacking All. 23 | Also, because schools must teach the spirit of goodwill, the habit of helping others around you, every class should have this rule: students, if you bring software to class you may not keep it for yourself. 24 | The paradigm of competition is a race: by rewarding the winner, we encourage everyone to run faster. When capitalism really works this way, it does a good job; but its defenders are wrong in assuming it always works this way. 25 | The reason that a good citizen does not use such destructive means to become wealthier is that, if everyone did so, we would all become poorer from the mutual destructiveness. 26 | When I launched the development of the GNU system, I explicitly said the purpose of developing this system is so we can use our computers and have freedom, thus if you use some other free system instead but you have freedom, then it's a success. It's not popularity for our code but it's success for our goal. 27 | The desire to be rewarded for one's creativity does not justify depriving the world in general of all or part of that creativity. 28 | In the free/libre software movement, we develop software that respects users' freedom, so we and you can escape from software that doesn't. 29 | If programmers deserve to be rewarded for creating innovative programs, by the same token they deserve to be punished if they restrict the use of these programs. 30 | Control over the use of one's ideas really constitutes control over other people's lives; and it is usually used to make their lives more difficult. 31 | Proprietary software is an injustice. 32 | There is nothing wrong with wanting pay for work, or seeking to maximize one's income, as long as one does not use means that are destructive. 33 | If ebooks mean that readers' freedom must either increase or decrease, we must demand the increase. 34 | Software patents are dangerous to software developers because they impose monopolies on software ideas. 35 | In practice, the copyright system does a bad job of supporting authors, aside from the most popular ones. Other authors' principal interest is to be better known, so sharing their work benefits them as well as readers. 36 | In the US, you even lose legal rights if you store your data in a company's machines instead of your own. The police need to present you with a search warrant to get your data from you; but if they are stored in a company's server, the police can get it without showing you anything. 37 | One reason you should not use web applications to do your computing is that you lose control. 38 | The interesting thing about cloud computing is that we've redefined cloud computing to include everything that we already do. 39 | Facebook collects a lot of data from people and admits it. And it also collects data which isn't admitted. And Google does too. As for Microsoft, I don't know. But I do know that Windows has features that send data about the user. 40 | If there is a Like button in a page, Facebook knows who visited that page. And it can get IP address of the computer visiting the page even if the person is not a Facebook user. 41 | Portable phones are Stalin's dreams 42 | If you see someone drowning and you know how to swim and there's no one around, go save them... unless that someone is Bush 43 | Teaching kids to use Windows is like teaching kids to chew tobacco 44 | If you can find a host for me that has a friendly parrot, I will be very very glad. If you can find someone who has a friendly parrot I can visit with, that will be nice too. 45 | I do not eat breakfast. Please do not ask me any questions about what I will do breakfast. Please just do not bring it up. 46 | One situation where I do not need help, let alone supervision, is in crossing streets. I grew up in the middle of the world's biggest city, full of cars, and I have crossed streets without assistance even in the chaotic traffic of Bangalore and Delhi. Please just leave me alone when I cross streets. 47 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2017-10-13 11:15:34 -0500 using RuboCop version 0.50.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 1 10 | # Cop supports --auto-correct. 11 | Layout/EmptyLines: 12 | Exclude: 13 | - 'plugins/bot_admin.rb' 14 | 15 | # Offense count: 1 16 | # Cop supports --auto-correct. 17 | # Configuration parameters: EnforcedStyle, SupportedStyles. 18 | # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines 19 | Layout/EmptyLinesAroundClassBody: 20 | Exclude: 21 | - 'plugins/crypto.rb' 22 | 23 | # Offense count: 4 24 | # Cop supports --auto-correct. 25 | Layout/EmptyLinesAroundExceptionHandlingKeywords: 26 | Exclude: 27 | - 'old_plugins/btc.rb' 28 | - 'old_plugins/eth.rb' 29 | - 'old_plugins/ltc.rb' 30 | - 'plugins/crypto.rb' 31 | 32 | # Offense count: 2 33 | # Cop supports --auto-correct. 34 | # Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. 35 | Layout/ExtraSpacing: 36 | Exclude: 37 | - 'plugins/cute_faces.rb' 38 | 39 | # Offense count: 1 40 | # Cop supports --auto-correct. 41 | Layout/LeadingCommentSpace: 42 | Exclude: 43 | - 'plugins/yossarian_plugin.rb' 44 | 45 | # Offense count: 1 46 | # Cop supports --auto-correct. 47 | Layout/SpaceAfterComma: 48 | Exclude: 49 | - 'plugins/crypto.rb' 50 | 51 | # Offense count: 6 52 | # Cop supports --auto-correct. 53 | Layout/SpaceInsideArrayPercentLiteral: 54 | Exclude: 55 | - 'plugins/zalgo/zalgo_text.rb' 56 | 57 | # Offense count: 4 58 | # Cop supports --auto-correct. 59 | # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. 60 | # SupportedStyles: space, no_space, compact 61 | # SupportedStylesForEmptyBraces: space, no_space 62 | Layout/SpaceInsideHashLiteralBraces: 63 | Exclude: 64 | - 'plugins/cute_faces.rb' 65 | - 'plugins/zalgo/zalgo.rb' 66 | 67 | # Offense count: 4 68 | # Cop supports --auto-correct. 69 | # Configuration parameters: EnforcedStyle, SupportedStyles. 70 | # SupportedStyles: final_newline, final_blank_line 71 | Layout/TrailingBlankLines: 72 | Exclude: 73 | - 'plugins/duck_duck_go_search.rb' 74 | - 'plugins/user_mail/user_mail.rb' 75 | - 'plugins/zalgo/zalgo.rb' 76 | - 'plugins/zippy_quotes/zippy_quotes.rb' 77 | 78 | # Offense count: 134 79 | Lint/AmbiguousRegexpLiteral: 80 | Enabled: false 81 | 82 | # Offense count: 3 83 | Lint/NonLocalExitFromIterator: 84 | Exclude: 85 | - 'plugins/bot_admin.rb' 86 | - 'plugins/command_help.rb' 87 | 88 | # Offense count: 35 89 | Lint/RescueException: 90 | Enabled: false 91 | 92 | # Offense count: 2 93 | # Cop supports --auto-correct. 94 | Lint/StringConversionInInterpolation: 95 | Exclude: 96 | - 'plugins/user_intros/user_intros.rb' 97 | 98 | # Offense count: 19 99 | Lint/UriEscapeUnescape: 100 | Enabled: false 101 | 102 | # Offense count: 2 103 | Lint/UselessAssignment: 104 | Exclude: 105 | - 'plugins/duck_duck_go_search.rb' 106 | - 'plugins/link_titling.rb' 107 | 108 | # Offense count: 37 109 | Metrics/AbcSize: 110 | Max: 42 111 | 112 | # Offense count: 3 113 | # Configuration parameters: CountComments, ExcludedMethods. 114 | Metrics/BlockLength: 115 | Max: 40 116 | 117 | # Offense count: 4 118 | # Configuration parameters: CountBlocks. 119 | Metrics/BlockNesting: 120 | Max: 4 121 | 122 | # Offense count: 1 123 | # Configuration parameters: CountComments. 124 | Metrics/ClassLength: 125 | Max: 135 126 | 127 | # Offense count: 5 128 | Metrics/CyclomaticComplexity: 129 | Max: 10 130 | 131 | # Offense count: 116 132 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. 133 | # URISchemes: http, https 134 | Metrics/LineLength: 135 | Max: 290 136 | 137 | # Offense count: 35 138 | # Configuration parameters: CountComments. 139 | Metrics/MethodLength: 140 | Max: 35 141 | 142 | # Offense count: 5 143 | Metrics/PerceivedComplexity: 144 | Max: 15 145 | 146 | # Offense count: 1 147 | # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. 148 | # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS 149 | Naming/FileName: 150 | Exclude: 151 | - 'yossarian-bot.rb' 152 | 153 | # Offense count: 1 154 | # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. 155 | # NamePrefix: is_, has_, have_ 156 | # NamePrefixBlacklist: is_, has_, have_ 157 | # NameWhitelist: is_a? 158 | Naming/PredicateName: 159 | Exclude: 160 | - 'spec/**/*' 161 | - 'plugins/zalgo/zalgo_text.rb' 162 | 163 | # Offense count: 4 164 | # Cop supports --auto-correct. 165 | Performance/Casecmp: 166 | Exclude: 167 | - 'plugins/cute_faces.rb' 168 | - 'plugins/last_seen.rb' 169 | - 'plugins/user_mail/user_mail.rb' 170 | 171 | # Offense count: 4 172 | # Cop supports --auto-correct. 173 | Performance/RegexpMatch: 174 | Exclude: 175 | - 'plugins/cstopic.rb' 176 | 177 | # Offense count: 5 178 | Style/ClassVars: 179 | Exclude: 180 | - 'plugins/user_quotes/user_quotes.rb' 181 | 182 | # Offense count: 95 183 | Style/Documentation: 184 | Enabled: false 185 | 186 | # Offense count: 51 187 | # Configuration parameters: EnforcedStyle, SupportedStyles. 188 | # SupportedStyles: annotated, template 189 | Style/FormatStringToken: 190 | Enabled: false 191 | 192 | # Offense count: 8 193 | # Configuration parameters: MinBodyLength. 194 | Style/GuardClause: 195 | Exclude: 196 | - 'plugins/channel_moderator/channel_moderator.rb' 197 | - 'plugins/ctcp_version.rb' 198 | - 'plugins/custom_triggers/custom_triggers.rb' 199 | - 'plugins/megahal.rb' 200 | - 'plugins/regex_replace.rb' 201 | - 'plugins/user_intros/user_intros.rb' 202 | - 'plugins/user_mail/user_mail.rb' 203 | - 'plugins/user_quotes/user_quotes.rb' 204 | 205 | # Offense count: 4 206 | # Cop supports --auto-correct. 207 | Style/MutableConstant: 208 | Exclude: 209 | - 'plugins/catch22.rb' 210 | - 'plugins/cute_faces.rb' 211 | - 'plugins/magic8ball.rb' 212 | - 'plugins/rainbow_text.rb' 213 | 214 | # Offense count: 1 215 | # Cop supports --auto-correct. 216 | # Configuration parameters: Whitelist. 217 | # Whitelist: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with 218 | Style/NestedParenthesizedCalls: 219 | Exclude: 220 | - 'plugins/phone_info.rb' 221 | 222 | # Offense count: 2 223 | # Cop supports --auto-correct. 224 | # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. 225 | # SupportedStyles: skip_modifier_ifs, always 226 | Style/Next: 227 | Exclude: 228 | - 'plugins/bot_admin.rb' 229 | 230 | # Offense count: 2 231 | # Cop supports --auto-correct. 232 | # Configuration parameters: Strict. 233 | Style/NumericLiterals: 234 | MinDigits: 7 235 | 236 | # Offense count: 3 237 | # Cop supports --auto-correct. 238 | # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. 239 | # SupportedStyles: predicate, comparison 240 | Style/NumericPredicate: 241 | Exclude: 242 | - 'spec/**/*' 243 | - 'old_plugins/eth.rb' 244 | - 'plugins/crypto.rb' 245 | - 'plugins/jerkcity.rb' 246 | 247 | # Offense count: 3 248 | # Cop supports --auto-correct. 249 | # Configuration parameters: PreferredDelimiters. 250 | Style/PercentLiteralDelimiters: 251 | Exclude: 252 | - 'plugins/zalgo/zalgo_text.rb' 253 | 254 | # Offense count: 2 255 | # Cop supports --auto-correct. 256 | Style/PerlBackrefs: 257 | Exclude: 258 | - 'extend/string.rb' 259 | 260 | # Offense count: 4 261 | # Cop supports --auto-correct. 262 | Style/Proc: 263 | Exclude: 264 | - 'plugins/channel_moderator/channel_moderator.rb' 265 | - 'plugins/custom_triggers/custom_triggers.rb' 266 | - 'plugins/user_mail/user_mail.rb' 267 | - 'plugins/user_points/user_points.rb' 268 | 269 | # Offense count: 4 270 | # Cop supports --auto-correct. 271 | Style/RedundantSelf: 272 | Exclude: 273 | - 'extend/string.rb' 274 | 275 | # Offense count: 7 276 | # Cop supports --auto-correct. 277 | # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. 278 | # SupportedStyles: slashes, percent_r, mixed 279 | Style/RegexpLiteral: 280 | Exclude: 281 | - 'old_plugins/google_search.rb' 282 | - 'plugins/channel_moderator/channel_moderator.rb' 283 | - 'plugins/isitup.rb' 284 | - 'plugins/regex_replace.rb' 285 | - 'plugins/taco_recipes.rb' 286 | 287 | # Offense count: 6 288 | # Cop supports --auto-correct. 289 | # Configuration parameters: ConvertCodeThatCanStartToReturnNil. 290 | Style/SafeNavigation: 291 | Exclude: 292 | - 'extend/opped.rb' 293 | - 'plugins/artist_info.rb' 294 | - 'plugins/bot_admin.rb' 295 | - 'plugins/link_titling.rb' 296 | - 'plugins/wolfram_alpha.rb' 297 | 298 | # Offense count: 7 299 | # Cop supports --auto-correct. 300 | # Configuration parameters: EnforcedStyle, SupportedStyles. 301 | # SupportedStyles: single_quotes, double_quotes 302 | Style/StringLiteralsInInterpolation: 303 | Exclude: 304 | - 'plugins/artist_info.rb' 305 | - 'plugins/exchange_rates.rb' 306 | - 'plugins/giphy.rb' 307 | - 'plugins/taco_recipes.rb' 308 | 309 | # Offense count: 2 310 | Style/StructInheritance: 311 | Exclude: 312 | - 'plugins/last_seen.rb' 313 | - 'plugins/user_mail/user_mail.rb' 314 | 315 | # Offense count: 3 316 | # Cop supports --auto-correct. 317 | # Configuration parameters: MinSize, SupportedStyles. 318 | # SupportedStyles: percent, brackets 319 | Style/SymbolArray: 320 | EnforcedStyle: brackets 321 | 322 | # Offense count: 1 323 | # Cop supports --auto-correct. 324 | # Configuration parameters: IgnoredMethods. 325 | # IgnoredMethods: respond_to, define_method 326 | Style/SymbolProc: 327 | Exclude: 328 | - 'plugins/channel_admin.rb' 329 | 330 | # Offense count: 3 331 | # Cop supports --auto-correct. 332 | Style/UnneededInterpolation: 333 | Exclude: 334 | - 'plugins/channel_admin.rb' 335 | - 'plugins/reminders.rb' 336 | 337 | # Offense count: 3 338 | # Cop supports --auto-correct. 339 | # Configuration parameters: SupportedStyles, WordRegex. 340 | # SupportedStyles: percent, brackets 341 | Style/WordArray: 342 | EnforcedStyle: percent 343 | MinSize: 9 344 | --------------------------------------------------------------------------------