diff --git a/.gitignore b/.gitignore index 8d6a243f..2a35b04d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,7 @@ build-iPhoneSimulator/ # Ignore cassette files /specs/cassettes/ + +# Ignore test file +test.rb + diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..709b5afc --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,38 @@ +require_relative "recipient" +require 'dotenv' +Dotenv.load + +module SlackCLI + class Channel < Recipient + attr_reader :topic, :member_count + + def initialize(slack_id:, name:, topic:, member_count:) + super(slack_id: slack_id, name: name) + + @topic = topic + @member_count = member_count + end + + def self.list + url = "https://slack.com/api/channels.list" + data = { + "token": ENV["SLACK_API_TOKEN"], + } + response = self.get(url, data) + channels = [] + response["channels"].each do |channel| + slack_id = channel["id"] + name = channel["name"] + topic = channel["topic"]["value"] + member_count = channel["num_members"] + channel = self.new(slack_id: slack_id, name: name, topic: topic, member_count: member_count) + channels << channel + end + return channels + end + + def details + return "Slack ID: #{slack_id}, Name: #{name}, Topic: #{topic}, Member count: #{member_count}" + end + end +end diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..a0b6c908 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,51 @@ +require "httparty" + +module SlackCLI + class Recipient + class SlackApiError < StandardError; end + + attr_reader :slack_id, :name + + def initialize(slack_id:, name:) + @slack_id = slack_id + @name = name + end + + def self.get(url, params) + response = HTTParty.get(url, query: params) + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, "Error when getting response, error: #{response.parsed_response["error"]}" + end + return response + end + + def send_message(message) + url = "https://slack.com/api/chat.postMessage" + api_key = ENV["SLACK_API_TOKEN"] + + body = { + token: api_key, + text: message, + channel: self.slack_id, + } + headers = {"Content-Type" => "application/x-www-form-urlencoded"} + response = HTTParty.post(url, body: body, headers: headers) + + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, "Error when posting #{message} to #{self.slack_id}, error: #{response.parsed_response["error"]}" + end + + return true + end + + private + + def self.list + raise NotImplementedError, "Implement me in a child class!" + end + + def details + raise NotImplementedError, "Implement me in a child class!" + end + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..b2ee6878 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,90 @@ #!/usr/bin/env ruby +require_relative "workspace.rb" +require "table_print" + +# :nocov: def main puts "Welcome to the Ada Slack CLI!" - # TODO project + workspace = SlackCLI::Workspace.new + puts "There are #{workspace.channels.count} channels in this workspace" + puts "There are #{workspace.users.count} users in this workspace" + + puts "You can: List Users, List Channels, Select User, Select Channel, Details, Send Message, or Quit." + puts "Please enter what option you would like to take. You can quit by entering 'quit'." + response = gets.chomp.downcase + while response != "quit" + if response == "list users" + list_users(workspace) + elsif response == "list channels" + list_channels(workspace) + elsif response == "select user" + select_user_by_input(workspace) + elsif response == "select channel" + select_channel_by_input(workspace) + elsif response == "details" + puts get_details(workspace) + elsif response == "send message" + send_message(workspace) + else + puts "Please make sure to select from the choices above. You can quit be entering 'quit'." + end + puts "\nPlease enter what you would like to do: \nList Users \nList Channels \nSelect User \nSelect Channel \nDetails \nSend Message \nQuit" + puts + response = gets.chomp.downcase + end puts "Thank you for using the Ada Slack CLI" end -main if __FILE__ == $PROGRAM_NAME \ No newline at end of file +def list_users(workspace) + tp workspace.users, "slack_id", "name", "real_name" +end + +def list_channels(workspace) + tp workspace.channels, "slack_id", "name", "topic", "member_count" +end + +def select_user_by_input(workspace) + puts "What is the Slack ID or username? " + user_info = gets.chomp + user = workspace.select_user(user_info) + if user == nil + puts "We couldn't find that user" + else + puts "User has been selected" + end +end + +def select_channel_by_input(workspace) + puts "What is the Slack ID or name? " + channel_info = gets.chomp + channel = workspace.select_channel(channel_info) + if channel == nil + puts "We couldn't find that channel" + else + puts "Channel has been selected" + end +end + +def get_details(workspace) + detail_info = workspace.show_details + if detail_info == nil + puts "There is nothing selected to show details about. Please select a user or channel." + else + return detail_info + end +end + +def send_message(workspace) + puts "What would you like to send?" + message = gets.chomp + if workspace.send_message(message) + puts "Message successfully sent" + else + puts "Please select a channel or user before sending message." + end +end +# :nocov: +main if __FILE__ == $PROGRAM_NAME diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..114c00e9 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,36 @@ +require_relative "recipient" +require "dotenv" +Dotenv.load + +module SlackCLI + class User < Recipient + attr_reader :real_name + + def initialize(slack_id:, name:, real_name:) + super(slack_id: slack_id, name: name) + + @real_name = real_name + end + + def self.list + url = "https://slack.com/api/users.list" + data = { + "token": ENV["SLACK_API_TOKEN"], + } + response = self.get(url, data) + users = [] + response["members"].each do |member| + slack_id = member["id"] + name = member["name"] + real_name = member["real_name"] + user = self.new(slack_id: slack_id, name: name, real_name: real_name) + users << user + end + return users + end + + def details + return "Slack ID: #{slack_id}, Username: #{name}, Real Name: #{real_name}" + end + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..7997c5ff --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,52 @@ +require_relative "user" +require_relative "channel" + +module SlackCLI + class Workspace + attr_reader :users, :channels, :selected + + def initialize + @users = SlackCLI::User.list + @channels = SlackCLI::Channel.list + @selected = nil + end + + def select_user(user_info) + @selected = find_user(user_info) + end + + def select_channel(channel_info) + @selected = find_channel(channel_info) + end + + def show_details + return @selected == nil ? nil : @selected.details + end + + def send_message(message) + return false if @selected == nil + @selected.send_message(message) + return true + end + + private + + def find_user(user_info) + @users.each do |user| + if user_info == user.slack_id || user_info == user.name + return user + end + end + return nil + end + + def find_channel(channel_info) + @channels.each do |channel| + if channel_info == channel.slack_id || channel_info == channel.name + return channel + end + end + return nil + end + end +end diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..3096fb47 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,55 @@ +require_relative "test_helper" + +describe SlackCLI::Channel do + describe "constructor" do + before do + @slack_id = "CH2SKTDBN" + @name = "Catssss" + @topic = "It's about cats, duh" + @member_count = "2,000,000" + @record = SlackCLI::Channel.new(slack_id: @slack_id, name: @name, topic: @topic, member_count: @member_count) + end + + it "is an instance of Channel" do + expect(@record).must_be_kind_of SlackCLI::Channel + end + + it "takes and saves a Slack id, name, topic, and member count" do + expect(@record.slack_id).must_equal @slack_id + expect(@record.name).must_equal @name + expect(@record.topic).must_equal @topic + expect(@record.member_count).must_equal @member_count + end + end + + describe "self.list" do + it "returns an array of channels" do + VCR.use_cassette("channels") do + channels_array = SlackCLI::Channel.list + + expect(channels_array).must_be_kind_of Array + expect(channels_array.first).must_be_kind_of SlackCLI::Channel + expect(channels_array.length).wont_equal 0 + end + end + end + + describe "details" do + before do + @slack_id = "CH2SKTDBN" + @name = "Catssss" + @topic = "It's about cats, duh" + @member_count = "2,000,000" + @record = SlackCLI::Channel.new(slack_id: @slack_id, name: @name, topic: @topic, member_count: @member_count) + @detail = @record.details + end + + it "returns a string" do + expect(@detail).must_be_kind_of String + end + + it "returns the correct information" do + expect(@detail).must_equal "Slack ID: #{@slack_id}, Name: #{@name}, Topic: #{@topic}, Member count: #{@member_count}" + end + end +end diff --git a/specs/recipient_spec.rb b/specs/recipient_spec.rb new file mode 100644 index 00000000..791869ea --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1,66 @@ +require_relative "test_helper" + +describe SlackCLI::Recipient do + before do + @slack_id = "CH2SKTDBN" + @name = "random" + @record = SlackCLI::Recipient.new(slack_id: @slack_id, name: @name) + end + describe "constructor" do + it "is an instance of Recipient" do + expect(@record).must_be_kind_of SlackCLI::Recipient + end + + it "takes and saves a Slack id and name" do + expect(@record.slack_id).must_equal @slack_id + expect(@record.name).must_equal @name + end + end + + describe "self.get" do + it "gets a response" do + VCR.use_cassette("recipient") do + url = "http://slack.com/api/users.list" + params = {"token": ENV["SLACK_API_TOKEN"]} + + response = SlackCLI::Recipient.get(url, params) + expect(response).wont_be_nil + expect(response).must_be_kind_of HTTParty::Response + expect(response.code).wont_equal 401 + end + end + + it "raises an error when given bad params" do + VCR.use_cassette("recipient") do + url = "https://slack.com/api/users.list" + data = { + "token": "BAD TOKEN", + } + exception = expect { SlackCLI::Recipient.get(url, data) }.must_raise SlackCLI::Recipient::SlackApiError + expect(exception.message).must_equal "Error when getting response, error: invalid_auth" + end + end + end + + describe "send message" do + it "can send a valid message" do + VCR.use_cassette("recipient") do + response = @record.send_message("Hey I can post messages!") + expect(response).must_equal true + end + end + + it "will raise an error when given an invalid channel" do + VCR.use_cassette("recipient") do + slack_id = "invalid_id" + name = "invalid-channel" + bad_record = SlackCLI::Recipient.new(slack_id: slack_id, name: name) + exception = expect { + bad_record.send_message("This post should not work") + }.must_raise SlackCLI::Recipient::SlackApiError + + expect(exception.message).must_equal "Error when posting This post should not work to invalid_id, error: channel_not_found" + end + end + end +end diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..05508fe3 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,31 @@ -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/channel" +require_relative "../lib/user" +require_relative "../lib/recipient" +require_relative "../lib/slack" +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.default_cassette_options = { + :record => :new_episodes, + :match_requests_on => [:method, :uri, :body], + } + 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..db13c452 --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,56 @@ +require_relative "test_helper" + +describe SlackCLI::User do + describe "constructor" do + before do + @slack_id = "CH2SKTDBN" + @name = "FreddyBoi13" + @real_name = "Alfred Molina" + @record = SlackCLI::User.new(slack_id: @slack_id, name: @name, real_name: @real_name) + end + + it "is an instance of User" do + expect(@record).must_be_kind_of SlackCLI::User + end + + it "takes and saves a Slack id, name, and real name" do + expect(@record.slack_id).must_equal @slack_id + expect(@record.name).must_equal @name + expect(@record.real_name).must_equal @real_name + end + end + + describe "class methods" do + before do + VCR.use_cassette("users") do + @users_array = SlackCLI::User.list + end + end + + describe "self.list" do + it "returns an array of users" do + expect(@users_array).must_be_kind_of Array + expect(@users_array.first).must_be_kind_of SlackCLI::User + expect(@users_array.length).wont_equal 0 + end + end + + describe "details" do + before do + @slack_id = "CH2SKTDBN" + @name = "FreddyBoi13" + @real_name = "Alfred Molina" + @record = SlackCLI::User.new(slack_id: @slack_id, name: @name, real_name: @real_name) + @detail = @record.details + end + + it "returns a string" do + expect(@detail).must_be_kind_of String + end + + it "returns the correct information" do + expect(@detail).must_equal "Slack ID: #{@slack_id}, Username: #{@name}, Real Name: #{@real_name}" + end + end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..7f09bab4 --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,164 @@ +require_relative "test_helper" + +describe "SlackCLI::Workspace" do + before do + VCR.use_cassette("workspace") do + @workspace = SlackCLI::Workspace.new + end + end + + describe "constructor" do + it "is an instance of Workspace" do + expect(@workspace).must_be_kind_of SlackCLI::Workspace + end + + it "has a collection of users, collection of channels, and a selected recipient is nil" do + expect(@workspace.users).must_be_kind_of Array + expect(@workspace.users.first).must_be_kind_of SlackCLI::User + expect(@workspace.channels).must_be_kind_of Array + expect(@workspace.channels.first).must_be_kind_of SlackCLI::Channel + expect(@workspace.selected).must_be_nil + end + end + + describe "select_user" do + it "should return an instance of User, given valid name" do + username = @workspace.users.first.name + expect(@workspace.select_user(username)).must_be_kind_of SlackCLI::User + expect(@workspace.selected).must_be_kind_of SlackCLI::User + expect(@workspace.selected).must_equal @workspace.users.first + end + + it "should return an instance of User, given valid slack id" do + slack_id = @workspace.users.first.slack_id + expect(@workspace.select_user(slack_id)).must_be_kind_of SlackCLI::User + expect(@workspace.selected).must_be_kind_of SlackCLI::User + expect(@workspace.selected).must_equal @workspace.users.first + end + + it "should update selected when a new user is selected" do + slack_id1 = @workspace.users.first.slack_id + slack_id2 = @workspace.users.last.slack_id + @workspace.select_user(slack_id1) + expect(@workspace.selected).must_equal @workspace.users.first + @workspace.select_user(slack_id2) + if slack_id1 != slack_id2 + expect(@workspace.selected).must_equal @workspace.users.last + end + end + + it "should return nil, given invalid user" do + bad_username = "" + @workspace.select_user(bad_username) + expect(@workspace.selected).must_be_nil + end + end + + describe "select_channel" do + it "should return an instance of Channel, given valid name" do + name = @workspace.channels.first.name + expect(@workspace.select_channel(name)).must_be_kind_of SlackCLI::Channel + expect(@workspace.selected).must_be_kind_of SlackCLI::Channel + expect(@workspace.selected).must_equal @workspace.channels.first + end + + it "should return an instance of Channel, given valid slack id" do + slack_id = @workspace.channels.first.slack_id + expect(@workspace.select_channel(slack_id)).must_be_kind_of SlackCLI::Channel + expect(@workspace.selected).must_be_kind_of SlackCLI::Channel + expect(@workspace.selected).must_equal @workspace.channels.first + end + + it "should update selected when a new channel is selected" do + slack_id1 = @workspace.channels.first.slack_id + slack_id2 = @workspace.channels.last.slack_id + @workspace.select_channel(slack_id1) + expect(@workspace.selected).must_equal @workspace.channels.first + @workspace.select_channel(slack_id2) + if slack_id1 != slack_id2 + expect(@workspace.selected).must_equal @workspace.channels.last + end + end + + it "should update selected from user to channel when new channel is selected" do + user_slack_id = @workspace.users.first.slack_id + channel_slack_id = @workspace.channels.first.slack_id + @workspace.select_user(user_slack_id) + expect(@workspace.selected).must_equal @workspace.users.first + @workspace.select_channel(channel_slack_id) + expect(@workspace.selected).must_equal @workspace.channels.first + end + + it "should return nil, given invalid channel" do + bad_slack_id = "" + @workspace.select_channel(bad_slack_id) + expect(@workspace.selected).must_be_nil + end + end + + describe "show details" do + it "displays details for user" do + username = @workspace.users.first.name + @workspace.select_user(username) + user_detail = @workspace.show_details + expect(user_detail).must_be_kind_of String + expect(@workspace.users.first.details).must_equal user_detail + end + + it "displays details for channel" do + channel_name = @workspace.channels.first.name + @workspace.select_channel(channel_name) + channel_detail = @workspace.show_details + expect(channel_detail).must_be_kind_of String + expect(@workspace.channels.first.details).must_equal channel_detail + end + + it "returns nil if selected is nil" do + if @workspace.selected == nil + expect(@workspace.show_details).must_be_nil + end + end + end + + describe "send_message" do + it "returns true if able to send message to user" do + VCR.use_cassette("workspace") do + username = @workspace.users.first.name + @workspace.select_user(username) + response = @workspace.send_message("Hey I can post messages!") + expect(response).must_equal true + end + end + + it "returns true if able to send message to channel" do + VCR.use_cassette("workspace") do + channel = @workspace.channels.first.name + @workspace.select_channel(channel) + response = @workspace.send_message("Hey I can post messages!") + expect(response).must_equal true + end + end + + it "returns false if selected is nil" do + VCR.use_cassette("workspace") do + response = @workspace.send_message("Hey I can post messages!") + expect(response).must_equal false + end + end + end + +end + + +# it "will raise an error when given an invalid channel" do +# VCR.use_cassette("workspace") do +# slack_id = "invalid_id" +# name = "invalid-channel" +# bad_record = SlackCLI::Recipient.new(slack_id: slack_id, name: name) +# exception = expect { +# bad_record.send_message("This post should not work") +# }.must_raise SlackCLI::Recipient::SlackApiError + +# expect(exception.message).must_equal "Error when posting This post should not work to invalid_id, error: channel_not_found" +# end +# end \ No newline at end of file