diff --git a/.gitignore b/.gitignore index 8d6a243f..b78e0506 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ /tmp/ # Used by dotenv library to load environment variables. -# .env +.env ## Specific to RubyMotion: .dat* diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..2faf7d15 --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,34 @@ +require_relative "recipient" + +module SlackCli + class Channel < SlackCli::Recipient + attr_reader :topic, :member_count + def initialize(slack_id, name, topic, member_count) + @slack_id = slack_id + @name = name + @topic = topic + @member_count = member_count + end + + def self.list_url + return "https://slack.com/api/channels.list" + end + + def self.list + response = get + channels = response["channels"].map do |channel| + slack_id = channel["id"] + name = channel["name"] + topic = channel["topic"]["value"] + member_count = channel["num_members"] + self.new(slack_id, name, topic, member_count) + end + return channels + end + + def details + deets = "Channel name: #{name} \nID: #{slack_id} \ntopic: #{topic}, \nMember count:#{member_count}\n" + return deets + end + end +end diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..27f095ad --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,67 @@ +require "httparty" +require "pry" + +require "dotenv" +Dotenv.load + +module SlackCli + class SlackError < StandardError; end + + class Recipient + MSG_URL = "https://slack.com/api/chat.postMessage" + TOKEN = ENV["SLACK_TOKEN"] + + attr_reader :send_message, :name, :slack_id, :error_helper, :details, :list + # :nocov: + def initialize + @slack_id = slack_id + @name = name + end + # :nocov: + + def self.list_url + raise NotImplementedError, "TODO: implement me in a child class" + end + + def post_message(name, message) + body_params = { + token: TOKEN, + as_user: true, + channel: name, + text: message, + } + response = HTTParty.post(MSG_URL, body: body_params) + + if response != 200 && !response["ok"] + raise SlackCli::SlackError "Error: #{response["error"]}" + else + return response + end + end + + def self.get + query_params = { + token: TOKEN, + } + response = HTTParty.get(list_url, query: query_params) + return error_helper(response) + end + + # :nocov: + def details + raise NotImplementedError "TODO: implement me in a child class" + end + + def self.list + raise NotImplementedError "TODO: implement me in a child class" + end + # :nocov: + + def self.error_helper(response) + unless response.code == 200 && response["ok"] + raise SlackError, "Error #{response.code}: #{response["error"]}" + end + return response + end + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..c04260fe 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,65 @@ -#!/usr/bin/env ruby +require_relative "workspace" +# :nocov: def main - puts "Welcome to the Ada Slack CLI!" + workspace = Workspace.new + puts "TatiHana Slack Channels loaded #{SlackCli::Channel.list.length} channels" + puts "TatiHana Users loaded #{SlackCli::User.list.length} users" - # TODO project + def options + puts "Please choose one of the following options: + - list users + - list channels + - select user + - select channel + - details + - send message + - quit + Enter your choice now:" + # selected = false + end - puts "Thank you for using the Ada Slack CLI" + options + choice = gets.chomp + loop do + case choice + when "list users" + puts workspace.list_users + when "list channels" + puts workspace.list_channels + when "select user" + puts "what user would you like to select?" + selected_user = gets.chomp + workspace.select_user(selected_user) + when "select channel" + puts "What channel would you like to select?" + selected_channel = gets.chomp + workspace.select_channel(selected_channel) + when "details" + begin + puts workspace.show_details + rescue + puts "No user or channel selected, try again." + end + when "send message" + if workspace.selected == nil + puts "No user or channel selected" + else + # begin + message = gets.chomp + workspace.send_message(message) + # rescue SlackCli::SlackError + puts "No user or channel selected, try again" + # end + end + when "quit" + puts "Thanks for checking out TatiHana! Bye bye..." + exit + end + options + choice = gets.chomp + end end -main if __FILE__ == $PROGRAM_NAME \ No newline at end of file +main if __FILE__ == $0 +# :nocov: diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..9ce3c4f3 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,45 @@ +require "pry" + +require_relative "recipient" + +module SlackCli + class User < Recipient + attr_reader :real_name, :status_text, :status_emoji + + def initialize(name, slack_id, real_name, status_text, status_emoji) + @name = name + @slack_id = slack_id + @real_name = real_name + @status_text = status_text + @status_emoji = status_emoji + end + + def self.list_url + return "https://slack.com/api/users.list" + end + + def self.list + response = get + + users = response["members"].map do |user| + name = user["name"] + slack_id = user["id"] + real_name = user["profile"]["real_name"] + status_text = user["profile"]["status_text"] + status_emoji = user["profile"]["status_emoji"] + self.new(name, slack_id, real_name, status_text, status_emoji) + end + + return users + end + + def details + user_details = "Username: #{name} + Slack ID: #{slack_id} + Real Name: #{real_name} + Status Text: #{status_text} + Status Emoji: #{status_emoji}" + return user_details + end + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..f9eec800 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,76 @@ +# workspace +require_relative "user" +require_relative "channel" +require "terminal-table" +require "pry" + +class Workspace + attr_reader :users, :channels, :selected, :list_users, :list_channels + + def initialize + @users = SlackCli::User.list + @channels = SlackCli::Channel.list + @selected = nil + end + + def select_channel(name) + @selected = @channels.find do |channel| + channel.name == name || channel.slack_id == name + end + if @selected == nil + puts "That channel does not exist" + else + return @selected + end + end + + def select_user(name) + @selected = @users.find do |user| + user.name == name || user.slack_id == name + end + + if @selected == nil + puts "That user does not exist" + else + return @selected + end + end + + def show_details + if @selected == nil + raise SlackCli::SlackError, "No user or channel selected!" + else + @selected.details + end + end + + def send_message(message) + if @selected == nil + raise SlackCli::SlackError, "No user or channel selected!" + else + message.strip! + if message.length == 0 + raise SlackCli::SlackError, "Message must cannot be nil or blank..." + else + @selected.post_message(@selected.slack_id, message) + end + end + end + + def list_channels + @channels.each do |channel| + puts "Channel name: #{channel.name} + ID: #{channel.slack_id} + Topic: #{channel.topic} + Member count:#{channel.member_count}" + end + return nil + end + + def list_users + @users.each do |user| + puts "#{user.real_name} Slack ID: #{user.slack_id}" + end + return nil + end +end diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..0e11fd12 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,38 @@ +require_relative "test_helper" + +describe "Channel" do + let(:get_response) do + VCR.use_cassette("slack/channel_get") { SlackCli::Channel.get } + end + let(:channel_list) do + VCR.use_cassette("slack/channel_list") { SlackCli::Channel.list } + end + + describe "self get method" do + it "successfully returns an HTTP response object" do + channels = get_response + + expect(channels["ok"]).must_equal true + end + end + + describe "list" do + it "creates a list of all channels" do + channels = channel_list + + expect(channels).must_be_kind_of Array + + channels.each do |channel| + expect(channel).must_be_instance_of SlackCli::Channel + end + end + end + + describe "details" do + it "lists details for an instance of Channel" do + channel = channel_list[1] + + expect(channel.details).must_be_kind_of String + end + end +end diff --git a/specs/recipient_spec.rb b/specs/recipient_spec.rb new file mode 100644 index 00000000..7ba6f805 --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1,56 @@ +require_relative "test_helper" + +# probably don't need any tests for abstract superclass. +describe "Recipient" do + before(:each) do + VCR.insert_cassette("recipient/api_calls") + end + after(:each) do + VCR.eject_cassette + end + let(:workspace) { + workspace = Workspace.new + } + + describe "self.get" do + describe "cases when our program should raise an exception..." do + it "should raise an exception created by changes to API" do + class Dummy < SlackCli::Recipient + BADTOKEN = "123" + + def self.list_url + return "http://slack.com/api/users.list" + end + + def self.get + query_params = { + token: BADTOKEN, + } + response = HTTParty.get(list_url, query: query_params) + return response + end + end + + test_dummy = Dummy.get + expect(test_dummy["ok"]).must_equal false + expect(test_dummy["error"]).must_equal "invalid_auth" + end + + it "should raise an exception created by changes to API URL" do + class Dummy < SlackCli::Recipient + def self.list_url + return "http://slack.com/api/users.lis" + end + def self.get + super + query_params = { + token: TOKEN, + } + end + end + + expect { Dummy.get }.must_raise SlackCli::SlackError + end + end + end +end diff --git a/specs/slack_spec.rb b/specs/slack_spec.rb new file mode 100644 index 00000000..550681f1 --- /dev/null +++ b/specs/slack_spec.rb @@ -0,0 +1 @@ +# AS WE LEARNED TODAY IN CLASS, WE DON'T NEED TO WRITE TESTS FOR DRIVER CODE!!! WOOOOOOOOO!!! \ No newline at end of file diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..affb5c80 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,30 @@ -require 'simplecov' -SimpleCov.start +require "simplecov" +SimpleCov.start do + add_filter %r{^/specs?/} +end -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_relative "../lib/slack" +require_relative "../lib/channel" +require_relative "../lib/user" +require_relative "../lib/workspace" +# require_relative "../lib/recipient" 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, # 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 + } + config.filter_sensitive_data("") do + ENV["SLACK_TOKEN"] + end +end diff --git a/specs/user_spec.rb b/specs/user_spec.rb new file mode 100644 index 00000000..b6646f4a --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,36 @@ +require_relative "test_helper" + +describe "User child class" do + let(:get_response) do + VCR.use_cassette("slack/user_get") { SlackCli::User.get } + end + let(:users_list) do + VCR.use_cassette("slack/user_list") { SlackCli::User.list } + end + + describe "user self.get method" do + it "successfully returns an HTTParty response object" do + users = get_response + + expect(users["ok"]).must_equal true + end + end + + describe "user self.list method" do + it "creates a list of all user objects" do + user_list = users_list + + expect(user_list).must_be_kind_of Array + expect(user_list.first).must_be_instance_of SlackCli::User + expect(user_list.last).must_be_instance_of SlackCli::User + end + end + + describe "details" do + it "lists details for an instance of User" do + user = users_list[1] + + expect(user.details).must_be_kind_of String + end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..7e04b536 --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,124 @@ +require_relative "test_helper" + +describe "Workspace class" do + before(:each) do + VCR.insert_cassette("workspace_response") + end + after(:each) do + VCR.eject_cassette + end + let(:workspace) { + workspace = Workspace.new + } + + describe "initialize" do + it "returns an array of User instances" do + expect(workspace.users).must_be_kind_of Array + expect(workspace.users.first).must_be_instance_of SlackCli::User + expect(workspace.users.last).must_be_instance_of SlackCli::User + end + + it "returns an array of Channel instances" do + expect(workspace.channels).must_be_kind_of Array + expect(workspace.channels.first).must_be_instance_of SlackCli::Channel + expect(workspace.channels.last).must_be_instance_of SlackCli::Channel + end + + it "@selected is nil prior to selecting a user or channel" do + assert_nil(workspace.selected) + end + end + + describe "select_channel method" do + it "returns selected instance of channel" do + expect(workspace.select_channel("everyone")).must_be_instance_of SlackCli::Channel + expect(workspace.select_channel("everyone").name).must_equal "everyone" + expect(workspace.selected.name).must_equal "everyone" + expect(workspace.selected.slack_id).must_equal "CH2RAKATW" + end + + it "returns nil if no channel selected" do + assert_nil(workspace.select_channel("")) + assert_nil(workspace.select_channel(nil)) + end + end + + describe "select_user method" do + it "returns selected instance of user" do + expect(workspace.select_user("hanalways")).must_be_instance_of SlackCli::User + expect(workspace.select_user("tatsqui").name).must_equal "tatsqui" + expect(workspace.selected.name).must_equal "tatsqui" + expect(workspace.selected.slack_id).must_equal "UH2NZ7X7V" + end + + it "returns nil if no user selected" do + assert_nil(workspace.select_user("")) + assert_nil(workspace.select_user(nil)) + end + end + + describe "send message" do + it "can post a message on the 'everyone' slack channel" do + workspace.select_channel("everyone") + text = "hello world" + pigeon = workspace.send_message(text) + + expect(pigeon["ok"]).must_equal true + end + end + + describe "show details method" do + it "returns details for a selected user" do + workspace.select_user("hanalways") + expect(workspace.show_details).must_be_kind_of String + end + + it "returns details for a selected channel" do + workspace.select_channel("everyone") + expect(workspace.show_details).must_be_kind_of String + end + + it "must raise SlackError for no selected user or channel" do + workspace.select_channel("") + expect { + workspace.show_details + }.must_raise SlackCli::SlackError + + workspace.select_user("") + expect { + workspace.show_details + }.must_raise SlackCli::SlackError + end + end + + describe "send_message method" do + it "raise an exception when a trying to send a nil or blank message to a channel or user" do + workspace.select_user("tatsqui") + no_text = "" + expect { workspace.send_message(no_text) }.must_raise SlackCli::SlackError + end + + it "will raise and exception with a nil message" do + message = nil + expect { workspace.send_message(message) }.must_raise SlackCli::SlackError + end + + it "successfully sends a message with selected user" do + workspace.select_user("tatsqui") + test_message = workspace.send_message("test") + expect(test_message["ok"]).must_equal true + end + end + + describe "list_channels method" do + it "returns details of a channel as nil" do + assert_nil(workspace.list_channels) + end + end + + describe "list_users method" do + it "returns details of a user as nil" do + assert_nil(workspace.list_users) + end + end +end