diff --git a/bot-settings.json b/bot-settings.json new file mode 100644 index 00000000..885447b1 --- /dev/null +++ b/bot-settings.json @@ -0,0 +1 @@ +{"username":"Maria Wissler"} \ No newline at end of file diff --git a/lib/apierror.rb b/lib/apierror.rb new file mode 100644 index 00000000..e65f8de8 --- /dev/null +++ b/lib/apierror.rb @@ -0,0 +1 @@ +class SlackApiError < StandardError; end diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..47e001a0 --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,32 @@ +require "pry" +require "httparty" + +class Channel < Recipient + attr_reader :topic, :member_count + + def initialize(name:, slack_id:, topic:, member_count:) + super(name: name, slack_id: slack_id) + @topic = topic + @member_count = member_count + end + + def self.list + response = self.get("channels.list") + channel_list = [] + response["channels"].each do |channel| + name = channel["name"] + slack_id = channel["id"] + topic = channel["topic"] + member_count = channel["members"].count + channel_list << self.new(name: name, slack_id: slack_id, topic: topic, member_count: member_count) + end + return channel_list + end + + def details + puts "Name: #{self.name}" + puts "ID: #{self.slack_id}" + puts "Topic: #{self.topic["value"]}" + puts "Number of members: #{self.member_count}" + end +end diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..a55d847f --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,45 @@ +require "pry" +require "httparty" + +class Recipient + BASE_URL = "https://slack.com/api/" + + attr_reader :slack_id, :name + + def initialize(slack_id:, name:) + @slack_id = slack_id + raise ArgumentError if !name.is_a? String + @name = name + end + + def send_message(params) + endpoint = "chat.postMessage" + url = BASE_URL + endpoint + params[:token] = ENV["SLACK_API_TOKEN"] + response = HTTParty.post(url, body: params) + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, response["error"] + end + return response + end + + private + + def self.get(endpoint, params = {}) + url = BASE_URL + endpoint + params[:token] = ENV["SLACK_API_TOKEN"] + response = HTTParty.get(url, query: params) + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, response["error"] + end + return response + end + + def self.list + raise NotImplementedError, "Implement me in a child class!" + end + + def self.details + raise NotImplementedError, "Implement me in a child class!" + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..9906d9c1 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,108 @@ #!/usr/bin/env ruby +require "pry" +require "httparty" +require "dotenv" +require_relative "../lib/workspace" +Dotenv.load + +def display_options + puts "\nChoose from one of the following:" + puts "------------------------------" + puts "\nSelect user" + puts "\nSelect channel" + puts "\nDetails" + puts "\nMessage" + puts "\nChange Settings" + puts "\nQuit" + option = gets.chomp.downcase + return verify_options(option) +end + +def verify_options(option) + options = ["select user", "select channel", "details", "message", "change settings", "quit"] + until options.include?(option) + puts "Please input a valid option." + option = display_options + end + return option +end def main puts "Welcome to the Ada Slack CLI!" + puts "\nWhat would you like to do?" - # TODO project + option = display_options + until option == "quit" + case option + when "select user" + puts "You chose to select a user. Please provide a username or Slack ID" + selected = gets.chomp() + workspace = Workspace.new(selected: selected) + recipient = workspace.select_user + until !recipient.nil? + puts "Please provide a valid username or Slack ID" + selected = gets.chomp + workspace = Workspace.new(selected: selected) + recipient = workspace.select_user + end + puts "You have selected #{recipient.real_name}" + puts "\nWhat would you like to do next?" + option = display_options + when "select channel" + puts "You chose to select a channel. Please provide a channel name or Slack ID" + selected = gets.chomp() + workspace = Workspace.new(selected: selected) + recipient = workspace.select_channel + until !recipient.nil? + puts "Please provide a valid Channel name or Slack ID" + selected = gets.chomp + workspace = Workspace.new(selected: selected) + recipient = workspace.select_channel + end + puts "You have selected #{recipient.name}" + option = display_options + when "details" + begin + workspace.show_details(recipient) + puts "\nWhat would you like to do next?" + option = display_options + rescue + puts "You must select a user or channel first." + puts "\nWhat would you like to do next?" + option = display_options + end + when "message" + begin + recipient.slack_id #checking if recipient exists; if it doesn't will throw name error => rescue clause + puts "What message would you like to send?" + message = gets.chomp + workspace.send_message(message, recipient) + puts "\nYou're message has been sent." + puts "\nWhat would you like to do next?" + option = display_options + rescue + puts "You must select a user or channel first." + puts "\nWhat would you like to do next?" + option = display_options + end + when "change settings" + puts "You can change the username displayed" + # puts "Please type either 'username' or 'icon emoji' or both to change either" + puts "What username would you like to use?" + setting_username_change = gets.chomp + params = {} + params[:username] = setting_username_change + Workspace.save_settings(params) + puts "Thanks, username is now #{setting_username_change}." + puts "Quit and restart the program for this change to be implemented" + option = display_options + end + end puts "Thank you for using the Ada Slack CLI" end -main if __FILE__ == $PROGRAM_NAME \ No newline at end of file +main if __FILE__ == $PROGRAM_NAME + +def verify_icon_emojis +end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..c9ae3521 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,35 @@ +require "pry" +require "httparty" +require_relative "recipient.rb" + +class User < Recipient + attr_reader :real_name, :status_text, :status_emoji + + def initialize(name:, slack_id:, real_name:, status_text: nil, status_emoji: nil) + super(name: name, slack_id: slack_id) + @real_name = real_name + @status_text = status_text + @status_emoji = status_emoji + end + + def self.list #factory method + response = self.get("users.list") + user_list = [] + response["members"].each do |member| + name = member["name"] + slack_id = member["id"] + real_name = member["real_name"] + status_text = member["profile"]["status_text"] + status_emoji = member["profile"]["status_emoji"] + user_list << self.new(name: name, slack_id: slack_id, real_name: real_name, status_text: status_text, status_emoji: status_emoji) + end + return user_list + end + + def details #business logic + puts "Username: #{self.name}" + puts "ID: #{self.slack_id}" + puts "Name: #{self.real_name}" + puts "Status: #{self.status_text}" + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..5b8b4ce8 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,57 @@ +require "httparty" +require_relative "../lib/user" +require_relative "../lib/channel" +require_relative "../lib/recipient" +require "json" + +class Workspace + attr_reader :users, :channels, :selected + + def initialize(selected:) + @users = User.list + @channels = Channel.list + @selected = selected # either user or channel info + end + + def select_user + user_selected = users.detect do |user| + user.slack_id == selected || user.name == selected + end + return user_selected + end + + def select_channel + channel_selected = channels.detect do |channel| + channel.slack_id == selected || channel.name == selected + end + return channel_selected + end + + def self.save_settings(params) + settings_file = File.open("bot-settings.json", "w") do |f| + f.write(params.to_json) + end + end + + def bot_settings_file_exist + begin + readfile = File.read("bot-settings.json") + return readfile + rescue + readfile = nil + end + end + + def send_message(message, recipient) + params = {} + params[:text] = message + params[:channel] = recipient.slack_id + readfile = bot_settings_file_exist #check if settings have been changed + params.merge!(eval(readfile)) if !readfile.nil? + recipient.send_message(params) + end + + def show_details(recipient) + recipient.details + end +end diff --git a/settings_slackapi.csv b/settings_slackapi.csv new file mode 100644 index 00000000..6e5ef4b6 --- /dev/null +++ b/settings_slackapi.csv @@ -0,0 +1 @@ +"{:username=>""cyndi""}" diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..aa142641 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,62 @@ +require_relative "test_helper" +require "pry" +describe "channel class" do + describe "initialize" do + it "creates and instance of channel" do + topic = "random" + member_count = 3 + name = "cyndilopez6" + slack_id = 1 + expect(Channel.new(name: name, slack_id: slack_id, topic: topic, member_count: member_count)).must_be_kind_of Channel + end + end + + describe "can connect to API" do + it "accesses api" do + VCR.use_cassette("connect to endpoints channels_list") do + endpoint = "channels.list" + @response = Channel.get(endpoint) + end + expect(@response.code == 200 && @response.parsed_response["ok"]).must_equal true + end + end + + describe "raises errors for incorrect endpoint" do + it "raises an error for incorrect endpoint" do + VCR.use_cassette("check_method_error_raised") do + endpoint = "ret424252E#1231+=.y" + exception = expect { Channel.get(endpoint) }.must_raise SlackApiError + expect(exception.message).must_equal "unknown_method" + end + end + + # it "raises an error for incorrect token" do + # VCR.use_cassette("check_auth_error_raised") do + # endpoint = "channels.list" + # params = {:token => "0123456789abcdef"} + # p params + # expect(Channel.get(endpoint, params)).must_equal "invalid_auth" + # end + # end + end + + describe "creates list of channels" do + it "returns a type of array" do + VCR.use_cassette("returns array") do + expect(Channel.list.is_a? Array).must_equal true + end + end + + it "returns an array of Channel objects" do + VCR.use_cassette("returns object Channel") do + expect(Channel.list[0]).must_be_kind_of Channel + end + end + + it "returns an accurate list of channels in slack workspace" do + VCR.use_cassette("correct channels") do + expect(Channel.list.map { |channel| channel.name }.length).must_be :>, 0 + end + end + end +end diff --git a/specs/recipient_spec.rb b/specs/recipient_spec.rb new file mode 100644 index 00000000..b077118b --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1,44 @@ +require_relative "test_helper" +require "pry" +describe "recipient class" do + describe "initialize" do + it "creates an instance of Recipient" do + #check slack_if format + slack_id = 1 + name = "Maria" + expect(Recipient.new(slack_id: slack_id, name: name)).must_be_kind_of Recipient + end + + it "raises an argument error if name is not a string" do + expect { Recipient.new(slack_id: 1, name: 21) }.must_raise ArgumentError + end + end + + describe "can connect to API" do + it "can connect" do + VCR.use_cassette("find channels") do + response = Recipient.get("channels.list") + expect(response["channels"]).wont_be_nil + expect(response["channels"].map { |channel| channel["name"] }.length).must_be :>, 0 + end + end + + it "gives a list with more than one user name" do + VCR.use_cassette("find channels") do + endpoint = "users.list" + response = Recipient.get(endpoint) + # Binding.pry + expect(response).wont_be_nil + expect(response["members"].map { |member| member["name"] }.length).must_be :>, 0 + end + end + it "can find the status of a member" do + VCR.use_cassette("user status") do + endpoint = "users.list" + response = Recipient.get(endpoint) + expect(response["members"][0]["profile"]["status_text"].length).wont_be_nil + expect(response["members"].select { |member| member["real_name"] == "Maria Wissler" }[0]["profile"]["status_text"]).must_be_kind_of String + end + end + end +end diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..2b796040 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,35 @@ -require 'simplecov' +require "simplecov" SimpleCov.start -require 'minitest' -require 'minitest/autorun' -require 'minitest/reporters' -require 'minitest/skip_dsl' -require 'vcr' +require "minitest" +require "minitest/autorun" +require "minitest/reporters" +require "minitest/skip_dsl" +require "vcr" +require "webmock/minitest" +require "dotenv" +Dotenv.load + +require_relative "../lib/slack" +require_relative "../lib/recipient" +require_relative "../lib/user" +require_relative "../lib/channel" +require_relative "../lib/apierror" +require_relative "../lib/workspace" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new VCR.configure do |config| - config.cassette_library_dir = "specs/cassettes" - config.hook_into :webmock -end \ No newline at end of file + config.cassette_library_dir = "specs/cassettes" # folder where casettes will be located + config.hook_into :webmock # tie into this other tool called webmock + config.default_cassette_options = { + :record => :new_episodes, # record new data when we don't have it yet + :match_requests_on => [:method, :uri, :body], # The http method, URI and body of a request all need to match + } + + # Don't leave our Slack token lying around in a cassette file. + + config.filter_sensitive_data("") do + ENV["SLACK_API_TOKEN"] + end +end diff --git a/specs/user_spec.rb b/specs/user_spec.rb new file mode 100644 index 00000000..3cd9f865 --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,75 @@ +require_relative "test_helper" +require "pry" +describe "user class" do + describe "initialize" do + it "creates and instance of user" do + name = "mcarmelina" + slack_id = 123 + real_name = "Maria Wissler" + status_text = "" + status_emoji = "" + expect(User.new(name: name, slack_id: slack_id, real_name: real_name, status_text: status_text, status_emoji: status_emoji)).must_be_kind_of User + end + end + + describe "can connect to API" do + before do + VCR.use_cassette("connect to endpoints users_list") do + endpoint = "users.list" + @response = User.get(endpoint) + expect(@response.code == 200 && @response.parsed_response["ok"]).must_equal true + end + end + it "gives a list of names" do + VCR.use_cassette("find members not empty") do + expect(@response).wont_be_nil + expect(@response["members"].map { |member| member["name"] }.length).must_be :>, 0 + end + end + it "finds the status of a member" do + VCR.use_cassette("user status") do + expect(@response["members"][0]["profile"]["status_text"].length).wont_be_nil + end + end + end + + describe "raises errors for incorrect endpoint" do + it "raises an error for incorrect endpoint" do + VCR.use_cassette("check_method_error_raised") do + endpoint = "ret424252E#1231+=.y" + exception = expect { User.get(endpoint) }.must_raise SlackApiError + expect(exception.message).must_equal "unknown_method" + end + end + end + + describe "create list of users" do + before do + VCR.use_cassette("self_list") do + @user_list = User.list + end + end + it "returns an array of users" do + VCR.use_cassette("list of users array") do + expect(@user_list).must_be_kind_of Array + end + end + it "contains instances of user within the array" do + VCR.use_cassette("instance of User within array") do + expect(@user_list[0]).must_be_kind_of User + end + end + + it "returns a list that is not empty" do + VCR.use_cassette("length user list") do + expect(@user_list.length).must_be :>, 0 + end + end + + it "returns name of first user correctly" do + VCR.use_cassette("bot user name") do + expect(@user_list.first.real_name).must_equal "Slackbot" + end + end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..024f796f --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,85 @@ +require_relative "test_helper" + +describe "workspace class" do + before do + VCR.use_cassette("create workspace object") do + selected = "USLACKBOT" #id of slackbot + @workspace = Workspace.new(selected: selected) + end + end + + describe "initialize" do + it "creates an instance of workspace" do + expect(@workspace).must_be_kind_of Workspace + end + end + + describe "#select_user" do + it "returns an instance of User" do + expect(@workspace.select_user).must_be_kind_of User + end + + it "returned the correct user" do + user = @workspace.select_user + expect(user.name).must_equal "slackbot" + end + + it "returns nil if username or slack id is not found" do + VCR.use_cassette("check nil returned") do + selected = "chewy" #id of nonexistent slackbot + @workspace2 = Workspace.new(selected: selected) + end + expect(@workspace2.select_user).must_be_nil + end + end + + describe "#selected_channel" do + before do + VCR.use_cassette("create workspace object") do + selected = "CH0ED08E4" #id for general channel + @workspace = Workspace.new(selected: selected) + end + end + it "returns an instance of Channel" do + expect(@workspace.select_channel).must_be_kind_of Channel + end + + it "returned the correct user" do + channel = @workspace.select_channel + expect(channel.name).must_be_kind_of String + end + + it "returns nil if username or slack id is not found" do + VCR.use_cassette("check nil returned") do + selected = "name your channel" + @workspace_channel = Workspace.new(selected: selected) + end + expect(@workspace_channel.select_channel).must_be_nil + end + end + + describe "#send_message" do + it "connects to the api ok" do + VCR.use_cassette("") do + selected = "CH0ED08E4" #id for general channel + @workspace = Workspace.new(selected: selected) + channel_selected = @workspace.select_channel + message = "hello" + response = @workspace.send_message(message, channel_selected) + expect(response.code).must_equal 200 + expect(response.parsed_response["ok"]).must_equal true + end + end + + it "returns an error if the message is empty" do + VCR.use_cassette("") do + selected = "CH0ED08E4" #id for general channel + @workspace = Workspace.new(selected: selected) + channel_selected = @workspace.select_channel + message = "" + exception = expect { @workspace.send_message(message, channel_selected) }.must_raise SlackApiError + expect(exception.message).must_equal "no_text" + end + end + end +end