diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..748cc155 --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,48 @@ +require "httparty" +require "dotenv" +require_relative "recipient" + +Dotenv.load + +module SlackCLI + class Channel < Recipient + BASE_URL = "https://slack.com/api/channels.list" + + attr_reader :topic, :members + + def initialize(slack_id, name, topic, members) + super(slack_id, name) + @topic = topic + @members = members + end + + def self.get_from_api + query_parameters = { + token: ENV["OAUTH_ACCESS_TOKEN"], + } + + response = get(BASE_URL, query_parameters) + + if (response.code == 200) + channels = response["channels"].map do |channel| + slack_id = channel["id"] + channel_name = channel["name"] + topic = channel["topic"]["value"] + members = channel["members"].length + new(slack_id, channel_name, topic, members) + end + return channels + else + raise SlackApiError, "Error #{response.code} : #{response["message"]}" + end + end + + def display_details + info_string = "\nSlack ID : #{slack_id}" + + "\nChannel name : #{name}" + + "\nTopic : #{topic}" + + "\nMember count: #{members}" + return info_string + end + end +end diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..4d3fb923 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,38 @@ +module SlackCLI + class Recipient + CHAT_ENDPOINT = "https://slack.com/api/chat.postMessage" + attr_reader :slack_id, :name + + def initialize(slack_id, name) + @name = name + @slack_id = slack_id + end + + def self.get(url, params) + return HTTParty.get(url, query: params) + end + + def send_message(message) + query_parameters = { + token: ENV["OAUTH_ACCESS_TOKEN"], + channel: slack_id, + text: message, + } + response = HTTParty.post( + CHAT_ENDPOINT, + body: query_parameters, + headers: { "Content-Type" => "application/x-www-form-urlencoded" }, + ) + + return response.code == 200 && response.parsed_response["ok"] + end + + def display_details + raise NotImplementedError, "Implement me in a child class!" + end + + def self.get_from_api + raise NotImplementedError, "Implement me in a child class!" + end + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..b4ef2de2 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,85 @@ #!/usr/bin/env ruby +require_relative "channel" +require_relative "user" +require_relative "workspace" + +require "httparty" +require "dotenv" +require "awesome_print" +require "colorize" + +Dotenv.load + +def set_main_menu_input + puts "What would you like to do?".colorize(:yellow) + puts "\nOptions: \n1.list users \n2.list channels \n3.select user \n4.select channel \n5.quit".colorize(:blue) + input = gets.chomp.downcase + return input +end + +def set_selected_input + puts "\nOptions: \ndetails \nsend message \nreturn to main menu".colorize(:blue) + input = gets.chomp.downcase + return input +end + +def selected_loop(input, workspace) + until (input == "return to main menu") + case input + when "details" + puts workspace.show_details.colorize(:green) + when "send message" + puts "Type a message to send:".colorize(:blue) + message = gets.chomp + workspace.send_message(message) + when "return to main menu" + break + else + puts "That wasn't one of the options!".colorize(:red) + end + input = set_selected_input + end +end def main - puts "Welcome to the Ada Slack CLI!" + users = SlackCLI::User.get_from_api + channels = SlackCLI::Channel.get_from_api + workspace = SlackCLI::Workspace.new(users: users, channels: channels) + + puts "Welcome to the Ada Slack CLI!".colorize(:yellow) + input = set_main_menu_input + + until (input == "quit") + case input + when "list users" + workspace.display_users + when "list channels" + workspace.display_channels + when "select user" + puts "Enter username or slack id:".colorize(:yellow) + + name_or_id = gets.chomp + workspace.select_user(name_or_id) + + input = set_selected_input + selected_loop(input, workspace) + when "select channel" + puts "Enter channel name or id:".colorize(:blue) + name_or_id = gets.chomp + workspace.select_channel(name_or_id) + + input = set_selected_input - # TODO project + selected_loop(input, workspace) + when "quit" + break + else + puts "That wasn't one of the options!".colorize(:red) + end + input = set_main_menu_input + end - puts "Thank you for using the Ada Slack CLI" + puts "Thank you for using the Ada Slack CLI.".colorize(:yellow) end -main if __FILE__ == $PROGRAM_NAME \ No newline at end of file +main if __FILE__ == $PROGRAM_NAME diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..c8907a24 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,46 @@ +require "httparty" +require "dotenv" +require "pry" +require "table_print" +require_relative "recipient" + +Dotenv.load + +module SlackCLI + class User < Recipient + BASE_URL = "https://slack.com/api/users.list" + attr_reader :real_name + + def initialize(name, real_name, slack_id) + super(slack_id, name) + @real_name = real_name + end + + def self.get_from_api + query_parameters = { + token: ENV["OAUTH_ACCESS_TOKEN"], + } + + response = get(BASE_URL, query_parameters) + + if (response.code == 200) + users = response["members"].map do |member| + name = member["name"] + real_name = member["real_name"] + slack_id = member["id"] + new(name, real_name, slack_id) + end + return users + else + raise SlackApiError, "Error #{response.code} : #{response["message"]}" + end + end + + def display_details + info_string = "\nSlack ID : #{slack_id}" + + "\nUsername : #{name}" + + "\nReal name : #{real_name}" + return info_string + end + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..3f840c47 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,56 @@ +require "httparty" +require "dotenv" +require "pry" + +Dotenv.load + +module SlackCLI + class SlackApiError < Exception; end + + class Workspace + attr_reader :users, :channels, :selected + + def initialize(users:, channels:) + @users = users + @channels = channels + end + + def display_users + return tp users, :name, :real_name, :slack_id + end + + def display_channels + return tp channels, :slack_id, :name, :members, :topic => { :width => 120 } + end + + def select_user(name_or_id) + @selected = users.find { |user| + user.name == name_or_id + } + + @selected ||= users.find { |user| + user.slack_id == name_or_id + } + + return @selected + end + + def select_channel(name_or_id) + @selected = channels.find { |channel| channel.name == name_or_id } + @selected ||= channels.find { |channel| channel.slack_id == name_or_id } + return @selected + end + + def show_details + selected.display_details + end + + def send_message(message) + if selected == nil + raise SlackApiError, "Invalid recipient" + else + selected.send_message(message) + end + end + end +end diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..bc3db4c0 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,46 @@ +require_relative "test_helper" + +describe "Channel" do + let (:channel_data) { + { + id: "CH2P2QWMR", + channel_name: "everyone", + topic: "Company-wide announcements and work-based matters", + members: 2, + } + } + it "instantiate a channel object" do + new_channel = SlackCLI::Channel.new( + channel_data[:id], + channel_data[:channel_name], + channel_data[:topic], + channel_data[:members] + ) + + expect(new_channel).must_be_instance_of SlackCLI::Channel + end + + it "has working reader methods" do + new_channel = SlackCLI::Channel.new( + channel_data[:id], + channel_data[:channel_name], + channel_data[:topic], + channel_data[:members] + ) + + expect(new_channel.slack_id).must_equal channel_data[:id] + expect(new_channel.name).must_equal channel_data[:channel_name] + expect(new_channel.topic).must_equal channel_data[:topic] + expect(new_channel.members).must_equal channel_data[:members] + end + + it "loads array of channels from Slack's API" do + VCR.use_cassette("list_channels") do + channels = SlackCLI::Channel.get_from_api + expect(channels).must_be_instance_of Array + channels.each do |channel| + expect(channel).must_be_instance_of SlackCLI::Channel + end + end + end +end diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..d844c0b1 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,30 @@ -require 'simplecov' -SimpleCov.start -require 'minitest' -require 'minitest/autorun' -require 'minitest/reporters' -require 'minitest/skip_dsl' -require 'vcr' +require "simplecov" +SimpleCov.start do + add_filter %r{^/specs?/} + add_filter "slack.rb" +end +require "minitest/autorun" +require "minitest/reporters" +require "vcr" +require "webmock/minitest" +require "dotenv" + +Dotenv.load + +require_relative "../lib/slack.rb" 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 token lying around in a cassette file. + config.filter_sensitive_data("") do + ENV["OAUTH_ACCESS_TOKEN"] + end +end diff --git a/specs/user_spec.rb b/specs/user_spec.rb new file mode 100644 index 00000000..6f5bcb7c --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,38 @@ +require_relative "test_helper" + +describe "User class" do + let (:user_data) { + { + username: "shubharajan", + real_name: "Shubha Rajan", + slack_id: "ADSHF1324123", + } + } + it "initializes a user class" do + new_user = SlackCLI::User.new(user_data[:username], + user_data[:real_name], + user_data[:slack_id]) + + expect(new_user).must_be_instance_of SlackCLI::User + end + + it "has working reader methods" do + new_user = SlackCLI::User.new(user_data[:username], + user_data[:real_name], + user_data[:slack_id]) + + expect(new_user.name).must_equal user_data[:username] + expect(new_user.real_name).must_equal user_data[:real_name] + expect(new_user.slack_id).must_equal user_data[:slack_id] + end + + it "loads array of users from Slack's API" do + VCR.use_cassette("list users") do + users = SlackCLI::User.get_from_api + expect(users).must_be_instance_of Array + users.each do |user| + expect(user).must_be_instance_of SlackCLI::User + end + end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..b11611d4 --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,94 @@ +require_relative "test_helper" + +describe "Workspace object" do + before do + VCR.use_cassette("workspace") do + @new_workspace = SlackCLI::Workspace.new( + users: SlackCLI::User.get_from_api, + channels: SlackCLI::Channel.get_from_api, + ) + end + end + + it "will initialize a new workspace object" do + expect(@new_workspace).must_be_instance_of SlackCLI::Workspace + end + + it "selects a user by username" do + user = @new_workspace.users.first + name = @new_workspace.users.first.name + expect(@new_workspace.select_user(name)).must_equal user + end + + it "selects a user by id" do + user = @new_workspace.users.first + id = @new_workspace.users.first.slack_id + expect(@new_workspace.select_user(id)).must_equal user + end + + it "selects a channel by name" do + channel = @new_workspace.channels.first + name = @new_workspace.channels.first.name + expect(@new_workspace.select_channel(name)).must_equal channel + end + + it "selects a channel by id" do + channel = @new_workspace.channels.first + id = @new_workspace.channels.first.slack_id + expect(@new_workspace.select_channel(id)).must_equal channel + end + + it "can send a valid message with id to a channel" do + VCR.use_cassette("post message") do + id = @new_workspace.channels.first.slack_id + @new_workspace.select_channel(id) + response = @new_workspace.send_message("This is a test!") + expect(response).must_equal true + end + end + + it "can send a valid message with id to a user" do + VCR.use_cassette("post message") do + id = @new_workspace.users.first.slack_id + @new_workspace.select_user(id) + response = @new_workspace.send_message("This is a test!") + expect(response).must_equal true + end + end + + it "can send a valid message with name to a channel" do + VCR.use_cassette("post message") do + name = @new_workspace.channels.first.name + @new_workspace.select_channel(name) + response = @new_workspace.send_message("This is a test!") + expect(response).must_equal true + end + end + + it "can send a valid message with name to a user" do + VCR.use_cassette("post message") do + name = @new_workspace.users.first.name + @new_workspace.select_user(name) + response = @new_workspace.send_message("This is a test!") + expect(response).must_equal true + end + end + + it "will raise an error if the channel is nil or invalid" do + VCR.use_cassette("post message") do + @new_workspace.select_channel("") + expect { + @new_workspace.send_message("This is a test!") + }.must_raise SlackCLI::SlackApiError + end + end + + it "will raise an error if the user is nil or invalid" do + VCR.use_cassette("post message") do + @new_workspace.select_user("") + expect { + @new_workspace.send_message("This is a test!") + }.must_raise SlackCLI::SlackApiError + end + end +end