diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..10f2f390 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Local File", + "type": "Ruby", + "request": "launch", + "cwd": "${workspaceRoot}", + "program": "${file}" + } + ] +} \ No newline at end of file diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..1ecb4f9d --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,40 @@ +require "dotenv" +require "HTTParty" +require "pry" + +require_relative "recipient" + +Dotenv.load + +module Slack + class Channel < Recipient + CHANNEL_URL = "https://slack.com/api/channels.list" + @query = { token: KEY } + + attr_reader :slack_id, :name, :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 + response = self.get(CHANNEL_URL, query: @query) + # binding.pry + channels = response["channels"].map do |channel| + id = channel["id"] + name = channel["name"] + topic = channel["topic"]["value"] + member_count = channel["num_members"] + self.new(slack_id: id, name: name, topic: topic, member_count: member_count) + 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..74506519 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,54 @@ +require "dotenv" +require "HTTParty" +require "pry" + +Dotenv.load +KEY = ENV["SLACK_TOKEN"] +MESSAGE_URL = "https://slack.com/api/chat.postMessage" + +module Slack + class SlackApiError < StandardError; end + + class Recipient + attr_reader :slack_id, :name + + def initialize(slack_id:, name:) + @slack_id = slack_id + @name = name + end + + def self.get(url, query:) + response = HTTParty.get(url, query: query) + if response["ok"] == false + raise SlackApiError.new("Invalid request, error #{response.code}: #{response.message}") + end + + return response + end + + def send_message(message) + body = { + token: KEY, + channel: slack_id, + text: message, + } + response = HTTParty.post(MESSAGE_URL, body: body) + + if response["ok"] == false + raise SlackApiError.new("Invalid request, error #{response.code}: #{response.message}") + end + + return response + end + + # private + + def self.list + raise NotImplementedError, "implement in child class" + end + + def details + raise NotImplementedError, "implement in child class" + end + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..85defc95 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,69 @@ #!/usr/bin/env ruby +require_relative "workspace" +require "table_print" + def main - puts "Welcome to the Ada Slack CLI!" + workspace = Slack::Workspace.new + puts "Welcome to the Ada Slack CLI! \n + There are #{workspace.channels.length} channels and #{workspace.users.length} users\n" + + commands = ["list users", "list channels", "select user", "select channel", "details", "send message", "quit"] - # TODO project + while true + puts "\nWhat would you like to do?" + puts + puts commands + puts + input = gets.chomp.downcase + + case input + when "list users" + tp workspace.users, :name, :slack_id, :real_name + when "list channels" + tp workspace.channels, :name, :topic, :slack_id, :member_count + when "select user" + puts "Please type in username or Slack ID" + user = gets.chomp + selected_user = workspace.select_user(user) + if selected_user + puts "You selected user #{selected_user.name}" + else + puts "This user does not exist" + end + when "select channel" + puts "Please type in channel name or Slack ID" + channel = gets.chomp + selected_channel = workspace.select_channel(channel) + if selected_channel + puts "You selected the channel called \'#{selected_channel.name}\'" + else + puts "This channel does not exist" + end + when "details" + if workspace.show_details == nil + puts "No user or channel is selected" + else + puts workspace.show_details + end + when "send message" + if workspace.selected == nil + puts "No user or channel selected. Try again" + else + puts "Write your message here" + message = gets.chomp + workspace.send_message(message) + end + when "quit" + break + else + puts "Command not valid. Please type one of the following" + end + end puts "Thank you for using the Ada Slack CLI" end -main if __FILE__ == $PROGRAM_NAME \ No newline at end of file +# Make sure all user input gets checked/converted for casing issues (.upcase for usernames, etc.) + +main if __FILE__ == $PROGRAM_NAME diff --git a/lib/slackapierror.rb b/lib/slackapierror.rb new file mode 100644 index 00000000..dde4abe8 --- /dev/null +++ b/lib/slackapierror.rb @@ -0,0 +1 @@ +SlackApiError < StandardError diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..1e402551 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,41 @@ +require "dotenv" +require "HTTParty" +require "pry" + +require_relative "recipient" + +Dotenv.load + +module Slack + class User < Recipient + USER_URL = "https://slack.com/api/users.list" + @query = { token: KEY } + + attr_reader :slack_id, :name, :real_name, :status_text, :status_emoji + + def initialize(slack_id:, name:, real_name:, status_text:, status_emoji:) + @slack_id = slack_id + @name = name + @real_name = real_name + @status_text = status_text + @status_emoji = status_emoji + end + + def self.list + response = self.get(USER_URL, query: @query) + users = response["members"].map do |user| + id = user["id"] + name = user["name"] + real_name = user["real_name"] + status_text = user["profile"]["status_text"] + status_emoji = user["profile"]["status_emoji"] + self.new(slack_id: id, name: name, real_name: real_name, status_text: status_text, status_emoji: status_emoji) + end + return users + end + + def details + return "slack_id: #{slack_id}, name: #{name}, real_name: #{real_name}, status_text: #{status_text}, status_emoji: #{status_emoji}" + end + end +end diff --git a/lib/verification.rb b/lib/verification.rb new file mode 100644 index 00000000..8bd976a9 --- /dev/null +++ b/lib/verification.rb @@ -0,0 +1,25 @@ +# Use the dotenv gem to load environment variables +# Use HTTParty to send a GET request to the channels.list endpoint +# Check that the request completed successfully, and print relevant information to the console if it didn't +# Loop through the results and print out the name of each channel + +require "dotenv" +require "HTTParty" +require "pry" + +Dotenv.load + +KEY = ENV["SLACK_TOKEN"] +url = "https://slack.com/api/channels.list" +query = { token: KEY } + +response = HTTParty.get(url, query: query) +binding.pry +if response.code != 200 + puts response.reason + puts response.code +else + response["channels"].each do |channel| + puts channel["name"] + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..660bf0c1 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,53 @@ +require_relative "user" +require_relative "channel" + +module Slack + class Workspace + attr_reader :users, :channels + attr_accessor :selected + + def initialize + @users = Slack::User.list + @channels = Slack::Channel.list + @selected = nil + end + + def select_user(value) + @users.each do |user| + if user.name == value + @selected = user + return user + elsif user.slack_id == value + @selected = user + return user + end + end + return nil + end + + def select_channel(value) + @channels.each do |channel| + if channel.name == value + @selected = channel + return channel + elsif channel.slack_id == value + @selected = channel + return channel + end + end + return nil + end + + def show_details + return selected.details if selected + end + + def send_message(message) + if selected + selected.send_message(message) + else + return nil + end + end + end +end diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..92b565c2 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,50 @@ +require_relative "test_helper" + +describe "Channel" do + describe "self.list" do + before do + VCR.use_cassette("make channels list") do + query_params = { + token: KEY, + } + @channels = Slack::Channel.list + @channel = @channels.first + end + end + + it "will return an array of Channels" do + expect(@channels).must_be_kind_of Array + expect(@channel).must_be_kind_of Slack::Channel + end + + it "will assign the correct attributes to each Channel" do + expect(@channel.slack_id).must_equal "CH2V3FHEY" + expect(@channel.name).must_equal "general" + expect(@channel.topic).must_equal "Company-wide announcements and work-based matters" + expect(@channel.member_count).must_equal 2 + end + + it "has the correct data tyes for each attribute" do + expect(@channel.slack_id).must_be_kind_of String + expect(@channel.name).must_be_kind_of String + expect(@channel.topic).must_be_kind_of String + expect(@channel.member_count).must_be_kind_of Integer + end + end + + describe "details" do + before do + VCR.use_cassette("show channel details") do + query_params = { + token: KEY, + } + @channels = Slack::Channel.list + @channel = @channels.first + end + end + + it "returns the correct data" do + 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..cfb19cfe --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1,79 @@ +require_relative "test_helper" + +describe "Recipient" do + describe "self.get method" do + before do + @url = "https://slack.com/api/channels.list" + end + it "successfully returns a response" do + VCR.use_cassette("get_data") do + query_params = { + token: KEY, + } + response = Slack::Recipient.get(@url, query: query_params) + expect(response["ok"]).must_equal true + end + end + + it "will raise an exception with incorrect token" do + VCR.use_cassette("get_data") do + query_params = { + token: "U R A TOKEN", + } + expect { + Slack::Recipient.get(@url, query: query_params) + }.must_raise Slack::SlackApiError + end + end + + it "will raise an exception with invalid url" do + VCR.use_cassette("get_data") do + query_params = { + token: KEY, + } + expect { + Slack::Recipient.get("abcdefg", query: query_params) + }.must_raise Addressable::URI::InvalidURIError + end + end + end + + describe "self.list" do + it "returns a notImplementedError" do + expect { + Slack::Recipient.list + }.must_raise NotImplementedError + end + end + + describe "details" do + it "returns a notImplementedError" do + recipient = Slack::Recipient.new(slack_id: 123, name: "hi") + expect { + recipient.details + }.must_raise NotImplementedError + end + end + + describe "send_message" do + before do + VCR.use_cassette("send_message") do + query_params = { + token: KEY, + } + url = "https://slack.com/api/channels.list" + response = Slack::Recipient.get(url, query: query_params) + slack_id = response["channels"].first["id"] + name = response["channels"].first["name"] + @recipient = Slack::Recipient.new(slack_id: slack_id, name: name) + end + end + + it "successfully sends a message" do + VCR.use_cassette("send_message") do + message = @recipient.send_message("HELLO WORLD!") + expect(message["ok"]).must_equal true + end + end + end +end diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..77e78fc8 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,30 @@ -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_relative "../lib/channel.rb" +require_relative "../lib/recipient.rb" +require_relative "../lib/slack.rb" +require_relative "../lib/user.rb" +require_relative "../lib/workspace.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.default_cassette_options = { + :record => :new_episodes, + :match_requests_on => [:method, :uri, :body], + } + + 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..33fff53e --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,52 @@ +require_relative "test_helper" + +describe "User" do + describe "self.list" do + before do + VCR.use_cassette("make users list") do + query_params = { + token: KEY, + } + @users = Slack::User.list + @user = @users.first + end + end + + it "will return an array of Users" do + expect(@users).must_be_kind_of Array + expect(@user).must_be_kind_of Slack::User + end + + it "will assign the correct attributes to each user" do + expect(@user.slack_id).must_equal "USLACKBOT" + expect(@user.name).must_equal "slackbot" + expect(@user.real_name).must_equal "Slackbot" + expect(@user.status_text).must_equal "" + expect(@user.status_emoji).must_equal "" + end + + it "has the correct data tyes for each attribute" do + expect(@user.slack_id).must_be_kind_of String + expect(@user.name).must_be_kind_of String + expect(@user.real_name).must_be_kind_of String + expect(@user.status_text).must_be_kind_of String + expect(@user.status_emoji).must_be_kind_of String + end + end + + describe "details" do + before do + VCR.use_cassette("show user details") do + query_params = { + token: KEY, + } + @users = Slack::User.list + @user = @users.first + end + end + + it "returns the correct data" do + 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..676cb8d9 --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,178 @@ +require_relative "test_helper" + +describe "Workspace class" do + describe "initialize method" do + it "will return expected user info" do + VCR.use_cassette("initialize workspace") do + query_params = { + token: KEY, + } + workspace = Slack::Workspace.new + workspace_first_user = workspace.users[0] + first_loaded_user = Slack::User.list.first + + expect(workspace).must_be_kind_of Slack::Workspace + expect(workspace.users).must_be_kind_of Array + expect(workspace_first_user).must_be_kind_of Slack::User + expect(workspace.users.last).must_be_kind_of Slack::User + expect(workspace_first_user.slack_id).must_equal first_loaded_user.slack_id + end + end + + it "will return expected channel info" do + VCR.use_cassette("initialize workspace") do + query_params = { + token: KEY, + } + workspace = Slack::Workspace.new + workspace_first_channel = workspace.channels[0] + first_loaded_channel = Slack::Channel.list.first + + expect(workspace).must_be_kind_of Slack::Workspace + expect(workspace.channels).must_be_kind_of Array + expect(workspace_first_channel).must_be_kind_of Slack::Channel + expect(workspace.channels.last).must_be_kind_of Slack::Channel + expect(workspace_first_channel.slack_id).must_equal first_loaded_channel.slack_id + end + end + end + + describe "select user method" do + before do + VCR.use_cassette("initialize workspace") do + query_params = { + token: KEY, + } + @workspace = Slack::Workspace.new + end + end + + it "will select a user based on user's name" do + name = "evelynnkaplan" + selected_user = @workspace.select_user(name) + + expect(selected_user).must_be_kind_of Slack::User + expect(selected_user.name).must_equal name + end + + it "will select a user based on user's id" do + id = "UH0J2E2D6" + selected_user = @workspace.select_user(id) + + expect(selected_user).must_be_kind_of Slack::User + expect(selected_user.slack_id).must_equal id + end + + it "returns nil if no user is found" do + id = "cat" + selected_user = @workspace.select_user(id) + + expect(selected_user).must_be_nil + end + end + + describe "select channel method" do + before do + VCR.use_cassette("initialize workspace") do + query_params = { + token: KEY, + } + @workspace = Slack::Workspace.new + end + end + + it "will select a channel based on channel's name" do + name = "random" + selected_channel = @workspace.select_channel(name) + + expect(selected_channel).must_be_kind_of Slack::Channel + expect(selected_channel.name).must_equal name + end + + it "will select a channel based on channel's id" do + id = "CH4DKF32S" + selected_channel = @workspace.select_channel(id) + + expect(selected_channel).must_be_kind_of Slack::Channel + expect(selected_channel.slack_id).must_equal id + end + + it "returns nil if no channel is found" do + id = "dog" + selected_channel = @workspace.select_channel(id) + + expect(selected_channel).must_be_nil + end + end + + describe "show details" do + before do + VCR.use_cassette("test details") do + query_params = { + token: KEY, + } + @workspace = Slack::Workspace.new + end + end + + it "details for selected recipient is string" do + @workspace.select_user("evelynnkaplan") + expect(@workspace.show_details).must_be_kind_of String + end + + it "returns nil if no recipient" do + expect(@workspace.show_details).must_be_nil + end + end + + describe "send_message" do + before do + VCR.use_cassette("workspace send message") do + query_params = { + token: KEY, + } + @workspace = Slack::Workspace.new + end + end + + it "successfully sends a message to a user" do + VCR.use_cassette("workspace send message") do + selected_user = @workspace.select_user("evelynnkaplan") + response = @workspace.send_message("sup dog") + + expect(response["ok"]).must_equal true + end + end + + it "doesn't send a message to a user that doesn't exist" do + VCR.use_cassette("workspace send message") do + selected_user = @workspace.select_user("ricksteves") + response = @workspace.send_message("sup dog") + + expect(response).must_be_nil + end + end + + it "successfully sends a message to a channel" do + VCR.use_cassette("workspace send message") do + selected_channel = @workspace.select_channel("random") + response = @workspace.send_message("sup dog") + + expect(response["ok"]).must_equal true + end + end + + it "doesn't send a message to a channel that doesn't exist" do + VCR.use_cassette("workspace send message") do + selected_channel = @workspace.select_channel("Rick Steves' channel") + response = @workspace.send_message("sup dog") + + expect(response).must_be_nil + end + end + + it "returns nil if there's no selected user or channel" do + expect(@workspace.send_message("sup dog")).must_be_nil + end + end +end