diff --git a/.gitignore b/.gitignore index 3ff4fada..646a3790 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /coverage/ - +/test/cassettes .DS_Store # Ignore environemnt variables diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..407cb918 --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,30 @@ +require_relative 'recipient.rb' +class Channel < Recipient + + def initialize(name, slack_id, topic, member_count) + super(name, slack_id) + @details["topic"] = topic + @details["num_members"] = member_count + end + + def topic + return details["topic"] + end + + def member_count + return details["num_members"] + end + + def self.make_query + return super("channel") + end + + def self.get_list + response = self.make_query + list = [] + response.parsed_response["channels"].each do |channel| + list << Channel.new(channel["name"], channel["id"], channel["topic"]["value"], channel["num_members"]) + end + return list + end +end \ No newline at end of file diff --git a/lib/lib_helper.rb b/lib/lib_helper.rb new file mode 100644 index 00000000..a9351763 --- /dev/null +++ b/lib/lib_helper.rb @@ -0,0 +1,15 @@ +require 'table_print' +require 'ap' +require 'HTTParty' +require 'dotenv' +Dotenv.load +Dotenv.require_keys("SLACK_TOKEN") + +API_KEY = ENV['SLACK_TOKEN'] +BASE_URL = 'https://slack.com/api/' +QUERY_PARAM = { + token: API_KEY +} + +require_relative "channel.rb" +require_relative "user.rb" \ No newline at end of file diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..54686261 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,35 @@ +require_relative 'lib_helper' +#template method for Channel and User +class API_Error < StandardError +end +class Recipient + attr_reader :details + attr_accessor :messages + def initialize(name, slack_id) + @details = { + "name" => name, + "id" => slack_id + } + @messages = {} + end + + def name + return @details["name"] + end + + def slack_id + return @details["id"] + end + + def self.make_query(recipient_kind) + if recipient_kind != "user" && recipient_kind != "channel" + raise ArgumentError.new("Not a valid recipient") + end + response = HTTParty.get(BASE_URL + "#{recipient_kind}s.list", query: QUERY_PARAM) + if !response["ok"] + raise API_Error.new("#{response["error"]}") + end + return response + end + +end diff --git a/lib/slack.rb b/lib/slack.rb index 8a0b659b..ca4d62f1 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,10 +1,65 @@ -#!/usr/bin/env ruby +require_relative 'workspace' + +def select(input, workspace) + recipient_kind = input.split(" ")[1] + puts "Enter the Slack ID, channel name, or username of the recipient." + recipient_id = gets.chomp + selected = workspace.select(recipient_kind, recipient_id) + if selected == nil + puts "Recipient not found." + else + puts "#{selected.class} #{selected.name} selected." + end + return selected +end def main puts "Welcome to the Ada Slack CLI!" workspace = Workspace.new - - # TODO project + puts "This workspace has #{workspace.users.length} users and #{workspace.channels.length} channels." + input = "" + selected = nil + while input != "quit" + puts "\nWhat would you like to do?" + puts "list users \nlist channels \nselect user \nselect channel \ndetails \nsend message \nmessage history \nquit \n\n" + input = gets.chomp + case input + when "list users" + workspace.print_list("users") + when "list channels" + workspace.print_list("channels") + when "select user" + selected = select(input, workspace) + when "select channel" + selected = select(input, workspace) + when "details" + if selected == nil + puts "You must select a recipient before asking for details." + else + ap selected.details + end + when "send message" + if selected == nil + puts "You must select a recipient before asking for details." + else + puts "What is your message to #{selected.name}?" + text = gets.chomp + workspace.post(text, selected) + puts "Your message to #{selected.name} was successfully sent." + end + when "message history" + if selected == nil + puts "You must select a recipient before asking for details." + else + puts "Messages sent to #{selected.name}:" + ap selected.messages.values + end + when "quit" + puts "Goodbye!" + else + puts "Oops, that's not valid input." + end + end puts "Thank you for using the Ada Slack CLI" end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..7778ddd8 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,25 @@ +require_relative 'recipient.rb' +class User < Recipient + + def initialize(name, slack_id, real_name) + super(name, slack_id) + @details["real_name"] = real_name + end + + def real_name + return details["real_name"] + end + + def self.make_query + return super("user") + end + + def self.get_list + response = self.make_query + list = [] + response.parsed_response["members"].each do |member| + list << User.new(member["name"], member["id"], member["real_name"]) + end + return list + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..4385c96b --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,65 @@ +require_relative "lib_helper.rb" +class Workspace + + attr_reader :users, :channels + def initialize + @users = User.get_list + @channels = Channel.get_list + end + + def select(recipient_kind, recipient_id) + case recipient_kind + when "user" + return find(users, recipient_id) + when "channel" + return find(channels, recipient_id) + else + raise ArgumentError.new("Invalid recipient.") + end + end + + def print_list(recipient) + case recipient + when "users" + tp users, {:name => {:display_name => "Username"}}, :slack_id, :real_name + when "channels" + tp channels, :name, :slack_id, :topic, :member_count + else + raise ArgumentError.new("Invalid recipient") + end + end + + def post(text, destination) + compose_message(text, destination) + response = HTTParty.post(BASE_URL + 'chat.postMessage', body: compose_message(text, destination)) + if !response["ok"] + raise API_Error.new("#{response["error"]}") + end + record_message(destination, response) + return response + end + + private + def compose_message(text, destination) + if !(destination.is_a? Recipient) + raise ArgumentError.new("Not a valid recipient") + end + return post_param = { + token: API_KEY, + channel: destination.slack_id, + text: text + } + end + + def find(recipient_kind, recipient_id) + selected = recipient_kind.find {|recipient| recipient.name == recipient_id || recipient.slack_id == recipient_id} + return selected + end + + def record_message(recipient, response) + time_stamp = response["ts"] + text = response["message"]["text"] + recipient.messages[time_stamp] = text + end + +end diff --git a/test/channel_test.rb b/test/channel_test.rb new file mode 100644 index 00000000..c72f2e7c --- /dev/null +++ b/test/channel_test.rb @@ -0,0 +1,61 @@ +require_relative 'test_helper.rb' + +describe 'Channel' do + describe 'constructor' do + before do + @channel = Channel.new("general", "W012A3CDE", "gen happenings", 3) + end + it 'creates channel, child of recipient' do + expect(@channel).must_be_kind_of Channel + expect(@channel.class.superclass).must_equal Recipient + end + it "has expected state" do + expect(@channel.name).must_equal "general" + expect(@channel.slack_id).must_equal "W012A3CDE" + expect(@channel.topic).must_equal "gen happenings" + expect(@channel.member_count).must_equal 3 + end + + end + + describe "get_list" do + before do + VCR.use_cassette("Channel.get_list") do + @channel_list = Channel.get_list + @target_channel = @channel_list.find {|channel| channel.name == "random"} + end + end + it "returns array" do + expect(@channel_list).must_be_kind_of Array + expect(@channel_list.empty?).must_equal false + expect(@channel_list.first).must_be_kind_of Channel + end + + it "has channel called Random" do + expect(@target_channel).must_be_kind_of Channel + end + + it "channel random has expected state" do + expect(@target_channel.slack_id).must_equal "CVBCU0R37" + expect(@target_channel.topic).must_equal "Non-work banter and water cooler conversation" + expect(@target_channel.member_count).must_equal 1 + end + + xit "sends message" do + VCR.use_cassette("channel-post-success") do + #test for failing POST is in recipient + response = @target_channel.send("hello, from bot") + expect(response["message"]["text"]).must_equal "hello, from bot" + end + end + end + + describe "details" do + it "displays correct details" do + channel_list = Channel.get_list + target_channel = channel_list.find {|channel| channel.name == "random"} + expect(target_channel.details["topic"]).must_equal "Non-work banter and water cooler conversation" + expect(target_channel.details["num_members"]).must_equal 2 + end + end +end \ No newline at end of file diff --git a/test/recipient_test.rb b/test/recipient_test.rb new file mode 100644 index 00000000..ae195bbc --- /dev/null +++ b/test/recipient_test.rb @@ -0,0 +1,56 @@ +require_relative "test_helper.rb" +describe "Recipient" do + describe "constructor" do + before do + @recipient = Recipient.new("Test", "T00000001") + end + it "creates Recipient" do + expect(@recipient).must_be_kind_of Recipient + end + it "has state" do + expect(@recipient.name).must_equal "Test" + expect(@recipient.slack_id).must_equal "T00000001" + end + end + + describe "self.details" do + it "returns array of name and id" do + recipient = Recipient.new("Test", "T00000001") + expect(recipient.details["id"]).must_equal "T00000001" + expect(recipient.details["name"]).must_equal "Test" + end + end + + describe "make_query" do + it "raises error for invalid recipient type" do + expect{Recipient.make_query("oogie-woogie")}.must_raise ArgumentError + end + + it "generates needed information for a user list" do + VCR.use_cassette("Recipient#make_query(user)") do + user_list = Recipient.make_query("user") + expect(user_list["ok"]).must_equal true + expect(user_list["members"]).must_be_kind_of Array + end + + end + + it "generates needed information for a channel list" do + VCR.use_cassette("Recipient#make_query(channel)") do + channel_list = Recipient.make_query("channel") + expect(channel_list["ok"]).must_equal true + expect(channel_list["channels"]).must_be_kind_of Array + end + + end + #how do I test for a bad API key??? + xit "throws API_Error when API_KEY is invalid" do + VCR.use_cassette("query fail") do + + expect { + + }.must_raise API_Error + end + end + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 1fcf2bab..4cc5d677 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -9,14 +9,25 @@ require 'minitest/skip_dsl' require 'vcr' +require 'dotenv' +Dotenv.load +Dotenv.require_keys("SLACK_TOKEN") + +require_relative '../lib/workspace.rb' +require_relative '../lib/slack.rb' +require_relative '../lib/channel.rb' +require_relative '../lib/recipient.rb' +require_relative '../lib/user.rb' + Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new -VCR.configure do |config| - config.cassette_library_dir = "test/cassettes" - config.hook_into :webmock -end +# VCR.configure do |config| +# config.cassette_library_dir = "test/cassettes" +# config.hook_into :webmock +# end VCR.configure do |config| + config.allow_http_connections_when_no_cassette = true config.cassette_library_dir = "test/cassettes" # folder where casettes will be located config.hook_into :webmock # tie into this other tool called webmock config.default_cassette_options = { @@ -25,5 +36,7 @@ } # Don't leave our token lying around in a cassette file. - + config.filter_sensitive_data("SLACK_TOKEN") do + ENV["SLACK_TOKEN"] + end end diff --git a/test/user_test.rb b/test/user_test.rb new file mode 100644 index 00000000..df4b17e0 --- /dev/null +++ b/test/user_test.rb @@ -0,0 +1,59 @@ +require_relative 'test_helper' + +describe 'User' do + describe 'constructor' do + before do + @user = User.new("Yaz", "W012A3CDE", "Yaz O'Shaughnessy") + end + it 'creates user, child of recipient' do + expect(@user).must_be_kind_of User + expect(@user.class.superclass).must_equal Recipient + end + it "has expected state" do + expect(@user.name).must_equal "Yaz" + expect(@user.slack_id).must_equal "W012A3CDE" + expect(@user.real_name).must_equal "Yaz O'Shaughnessy" + end + + end + + describe "details" do + it "displays correct details" do + VCR.use_cassette("User.get_list") do + user_list = User.get_list + target_user = user_list.find {|user| user.name == "slackbot"} + expect(target_user.details["real_name"]).must_equal "Slackbot" + end + end + end + + describe ".get_list + send" do + before do + VCR.use_cassette("User.get_list") do + @user_list = User.get_list + @target_user = selected = @user_list.find {|recipient| recipient.name == "slackbot"} + end + end + it "returns array" do + expect(@user_list).must_be_kind_of Array + expect(@user_list.empty?).must_equal false + expect(@user_list.first).must_be_kind_of User + end + + it "has user called SlackBot" do + expect(@target_user).must_be_kind_of User + end + + it "user SlackBot has expected state" do + expect(@target_user.slack_id).must_equal "USLACKBOT" + expect(@target_user.real_name).must_equal "Slackbot" + end + + it "sends message" do + VCR.use_cassette("post-success") do + #test for failing POST is in recipient + end + end + + end +end \ No newline at end of file diff --git a/test/workspace_test.rb b/test/workspace_test.rb new file mode 100644 index 00000000..4084fd6e --- /dev/null +++ b/test/workspace_test.rb @@ -0,0 +1,69 @@ +require_relative 'test_helper' + +describe 'Workspace' do + describe 'constructor' do + before do + VCR.use_cassette("workspace") do + @workspace = Workspace.new + end + end + it 'creates instance of Workspace' do + expect(@workspace).must_be_kind_of Workspace + end + it 'has state @users and @channels' do + expect(@workspace.users).must_be_kind_of Array + expect(@workspace.channels).must_be_kind_of Array + end + it "users contains users" do + expect(@workspace.users.first).must_be_kind_of User + target_user = @workspace.users.find {|user| user.name == "slackbot"} + expect(target_user).must_be_kind_of User + end + + it "channels contains channels" do + expect(@workspace.channels.first).must_be_kind_of Channel + target_channel = @workspace.channels.find {|channel| channel.name == "random"} + expect(target_channel).must_be_kind_of Channel + end + end + + describe "print_list" do + before do + VCR.use_cassette("workspace") do + @workspace = Workspace.new + end + end + it "throws ArgError when given invalid recipient" do + expect{@workspace.print_list("blue")}.must_raise ArgumentError + end + + end + + describe "select" do + before do + VCR.use_cassette("workspace") do + @workspace = Workspace.new + end + end + it "throws ArgError when given invalid recipient" do + expect{@workspace.select("oogie", "boogie")}.must_raise ArgumentError + end + it "returns a user" do + target_user = @workspace.select("user", "slackbot") + expect(target_user).must_be_kind_of User + end + it "returns array of channels" do + target_channel = @workspace.select("channel", "random") + expect(target_channel).must_be_kind_of Channel + end + end + + describe "post" do + it "raises API error" do + VCR.use_cassette("post-fail") do + bunk_rec = Recipient.new("Test", "T00000001") + expect{bunk_rec.post("hi", bunk_rec)}.must_raise API_Error + end + end + end +end \ No newline at end of file