diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..f85cfb4f --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,33 @@ +require "dotenv" +require "httparty" + +Dotenv.load + +module SlackApi + class SlackError < StandardError; end + + class Channel + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + + def self.channel_api(url, key) + parameters = { 'token': key } + response = HTTParty.get(url, query: parameters).to_s + response = JSON.parse(response) + + if response["ok"] == true + return response["channels"] + else + raise SlackApi::SlackError, "Error with Channel API: #{response["error"]}" + end + end + + def self.list(channels_list) + puts "\nHere are a list of your channels for this Workspace:" + list_of_channels = channels_list.map do |channel| + "\nChannel name: #{channel["name"]}\nSlack ID: #{channel["id"]}\nTopic: #{channel["topic"]["value"]}\nMember count: #{channel["num_members"]}\n" + end + return list_of_channels + end + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..192148ec 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,55 @@ #!/usr/bin/env ruby +require_relative "user" +require_relative "channel" +require_relative "workspace" + +Dotenv.load + def main + workspace = SlackApi::Workspace.new + + user_options = "list users\nlist channels\nselect user\nselect channel\ndetails\nsend message\nquit" + puts "Welcome to the Ada Slack CLI!" + puts "\nThere are #{workspace.channels.length} channels and #{workspace.users.length} users in this Workspace." + puts "\nWhat would you like to do?:" + puts user_options + + user_selection = gets.chomp + until user_selection == "quit" + case user_selection + when "list users" + puts SlackApi::User.list(workspace.users) + when "list channels" + puts SlackApi::Channel.list(workspace.channels) + when "select user" + print "\nEnter username or user's ID: " + user_input = gets.chomp + if workspace.select_user(user_input) != true + puts "\n~That user does not exist~" + end + when "select channel" + print "\nEnter channel name or channel's ID: " + user_input = gets.chomp + if workspace.select_channel(user_input) != true + puts "\n~That channel does not exist~" + end + when "details" + puts workspace.show_details + when "send message" + puts "\n(This will send to the recipient you have selected)" + print "What message would you like to send? " + user_message = gets.chomp + workspace.send_message(user_message) + end - # TODO project + puts "\nWhat would you like to do next?" + puts user_options + user_selection = gets.chomp + 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 diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..d0c68fd9 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,33 @@ +require "dotenv" +require "httparty" + +Dotenv.load + +module SlackApi + class SlackError < StandardError; end + + class User + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + def self.user_api(url, key) + parameters = { 'token': key } + response = HTTParty.get(url, query: parameters).to_s + response = JSON.parse(response) + + if response["ok"] == true + return response["members"] + else + raise SlackApi::SlackError, "Error with User API: #{response["error"]}" + end + end + + def self.list(users_list) + puts "\nHere are a list of your users for this Workspace:" + list_of_users = users_list.map do |user| + "\nUsername: #{user["name"]}\nSlack ID: #{user["id"]}\nReal name: #{user["real_name"]}" + end + return list_of_users + end + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..0ad3c6cc --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,81 @@ +require "dotenv" +require "httparty" + +require_relative "channel" +require_relative "user" + +Dotenv.load + +module SlackApi + class SlackError < StandardError; end + + class Workspace + attr_reader :channels, :users + + def initialize + @users = SlackApi::User.user_api("https://slack.com/api/users.list", ENV["SLACK_API_TOKEN"]) + @channels = SlackApi::Channel.channel_api("https://slack.com/api/channels.list", ENV["SLACK_API_TOKEN"]) + @selected = "" + end + + def select_channel(user_input) + @channels.each do |channel| + if channel["name"] == user_input || channel["id"] == user_input + @selected = channel + return true + end + end + return false + end + + def select_user(user_input) + @users.each do |user| + if user["name"] == user_input || user["id"] == user_input + @selected = user + return true + end + end + return false + end + + def show_details + if @channels.include?(@selected) + channel_details = "\nChannel name: #{@selected["name"]}\nSlack ID: #{@selected["id"]}\nTopic: #{@selected["topic"]["value"]}\nMember count: #{@selected["num_members"]}" + + return channel_details + elsif @users.include?(@selected) + user_details = "\nUsername: #{@selected["name"]}\nSlack ID: #{@selected["id"]} \nReal name: #{@selected["real_name"]}" + + return user_details + else + error_message = "\n~You have not selected a user or channel yet.~" + return error_message + end + end + + def send_message(message) + url = "https://slack.com/api/chat.postMessage" + key = ENV["SLACK_API_TOKEN"] + + if @channels.include?(@selected) || @users.include?(@selected) + response = HTTParty.post( + url, + headers: { "Content-Type" => "application/x-www-form-urlencoded" }, + body: { + token: key, + text: message, + channel: @selected["id"], + }, + ) + if response["ok"] + return true + else + raise SlackApi::SlackError, "Error when posting message to #{@selected["name"]}, error: #{response["error"]}" + end + else + error_message = "\n~You have not selected a user or channel yet.~" + return error_message + end + end + end +end diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..d331438a --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,90 @@ +require_relative "test_helper" + +describe SlackApi::Channel do + describe "json channels" do + it "returns valid channels" do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + channels_list.each do |channel| + expect(channel["is_channel"]).must_equal true + end + end + end + + it "return includes a specific channel" do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + expect(channels_list.first["name"]).must_equal "general" + end + end + + it "return includes a specific channel" do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + expect(channels_list.first["id"]).must_equal "CH2RC3CNQ" + end + end + + it "return includes a specific channel" do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + expect(channels_list.first["topic"]["value"]).must_equal "Company-wide announcements and work-based matters" + end + end + + it "return includes a specific channel" do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + expect(channels_list.first["num_members"]).must_equal 2 + end + end + + it "channels list will only include existent channels " do + VCR.use_cassette("slack_channels_json") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + channels_list.each do |channel| + name = "Not a Channel" + expect(channel["name"]).wont_equal name + end + end + end + + it "raises error when a bad API call is made" do + VCR.use_cassette("slack_channels_list") do + url = "https://slack.com/api/channels.list" + + expect { SlackApi::Channel.channel_api(url, "") }.must_raise SlackApi::SlackError + end + end + end + + describe "list channels" do + it "returns an array" do + VCR.use_cassette("slack_channels_list") do + url = "https://slack.com/api/channels.list" + key = ENV["SLACK_API_TOKEN"] + channels_list = SlackApi::Channel.channel_api(url, key) + + expect(SlackApi::Channel.list(channels_list)).must_be_instance_of Array + end + end + end +end diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..24c06e39 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,32 @@ -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 "dotenv" +Dotenv.load + +require "minitest" +require "minitest/autorun" +require "minitest/reporters" +require "minitest/skip_dsl" +require "webmock/minitest" +require "vcr" + +require_relative "../lib/channel" +require_relative "../lib/user" +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("SLACK_API_TOKEN") 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..e0452003 --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,85 @@ +require_relative "test_helper" + +describe SlackApi::User do + describe "json users" do + it "returns valid users" do + VCR.use_cassette("slack_users_json") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + + users_list.each do |user| + expect(user["team_id"]).wont_be_nil + end + end + end + + it "includes a specific username" do + VCR.use_cassette("slack_user_list") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + + expect(users_list.first["name"]).must_equal "slackbot" + end + end + + it "includes a specific user's real name" do + VCR.use_cassette("slack_user_list") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + + expect(users_list.first["real_name"]).must_equal "Slackbot" + end + end + + it "includes a specific user's slack id" do + VCR.use_cassette("slack_user_list") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + + expect(users_list.first["id"]).must_equal "USLACKBOT" + end + end + + it "users list will only include existent users" do + VCR.use_cassette("slack_users_json") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + + users_list.each do |user| + name = "Not a User" + expect(user["name"]).wont_equal name + end + end + end + + it "raises error when a bad API call is made" do + VCR.use_cassette("slack_users_list") do + url = "https://slack.com/api/users.list" + + expect { SlackApi::User.user_api(url, "") }.must_raise SlackApi::SlackError + end + end + end + + describe "list users" do + it "returns an array" do + VCR.use_cassette("slack_users_list") do + url = "https://slack.com/api/users.list" + key = ENV["SLACK_API_TOKEN"] + + users_list = SlackApi::User.user_api(url, key) + expect(SlackApi::User.list(users_list)).must_be_instance_of Array + end + end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..65a2ed0e --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,128 @@ +require_relative "test_helper" + +describe SlackApi::Workspace do + describe "select_channel" do + it "selects a valid channel name" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + valid_channel_name = "random" + expect(workspace.select_channel(valid_channel_name)).must_equal true + end + end + + it "selects a valid channel ID" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + valid_channel_id = "CH3UGLBHV" + expect(workspace.select_channel(valid_channel_id)).must_equal true + end + end + + it "returns false if an invalid channel is attempted to be selected" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + invalid_channel_name = "not a channel" + expect(workspace.select_channel(invalid_channel_name)).must_equal false + end + end + + it "returns a false if select_channel is not given an input" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + expect(workspace.select_channel("")).must_equal false + end + end + end + + describe "select_user" do + it "selects a valid username" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + valid_user_name = "slackbot" + expect(workspace.select_user(valid_user_name)).must_equal true + end + end + + it "selects a valid user ID" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + valid_user_id = "USLACKBOT" + expect(workspace.select_user(valid_user_id)).must_equal true + end + end + + it "returns a false if an invalid user is attempted to be selected" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + invalid_user_name = "smackbot" + expect(workspace.select_user(invalid_user_name)).must_equal false + end + end + + it "returns a false if select_user is not given an input" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + expect(workspace.select_user("")).must_equal false + end + end + end + + describe "show_details" do + it "returns details for the correct channel" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + workspace.select_channel("random") + + expect(workspace.show_details).must_include "CH3UGLBHV" + end + end + + it "returns details for the correct user" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + workspace.select_user("slackbot") + + expect(workspace.show_details).must_include "USLACKBOT" + end + end + + it "returns an error message if no recipient has been selected" do + VCR.use_cassette("slack_workspace") do + workspace = SlackApi::Workspace.new + + expect(workspace.show_details).must_include "You have not selected a user or channel yet." + end + end + end + + describe "send_message" do + it "can send a valid message to a channel" do + VCR.use_cassette("slack_message") do + workspace = SlackApi::Workspace.new + workspace.select_channel("general") + return_value = workspace.send_message("This is a message!") + + expect(return_value).must_equal true + end + end + + it "returns error message if no recipient has been selected yet" do + VCR.use_cassette("slack_message") do + workspace = SlackApi::Workspace.new + return_value = workspace.send_message("This is a message!") + + expect(return_value).must_include "You have not selected a user or channel yet." + end + end + + it "will raise an error if given an empty message" do + VCR.use_cassette("slack_message") do + workspace = SlackApi::Workspace.new + workspace.select_channel("general") + expect { + workspace.send_message("") + }.must_raise SlackApi::SlackError + end + end + end +end