diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..40c88a7b Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 8d6a243f..bd0ea0a8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.rbc /.config /coverage/ +*/coverage/ /InstalledFiles /pkg/ /spec/reports/ @@ -11,7 +12,7 @@ /tmp/ # Used by dotenv library to load environment variables. -# .env +.env ## Specific to RubyMotion: .dat* @@ -54,3 +55,5 @@ build-iPhoneSimulator/ # Ignore cassette files /specs/cassettes/ + +.vscode \ No newline at end of file diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 00000000..c2638d5c Binary files /dev/null and b/lib/.DS_Store differ diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..f203188c --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,49 @@ +require 'dotenv' +require 'httparty' +require 'table_print' +require_relative 'recipient' +Dotenv.load + +module SlackAPI + class Channel < Recipient + + attr_reader :topic, :member_count + + URL = "https://slack.com/api/channels.list" + TOKEN = ENV['TOKEN'] + @@all = [] + + def initialize(slack_id:, name:, topic:, member_count:) + super(slack_id: slack_id, name: name) + @topic = topic + @member_count = member_count + end + + def details + return "Channel name: #{name} + Slack ID: #{slack_id} + Topic: #{topic} + Member count: #{member_count}" + end + + def self.list + return @@all + end + + def self.load + query_parameters = { + token: TOKEN, + } + response = HTTParty.get(URL, query: query_parameters)['channels'] + response.each do |channel| + topic = channel['topic']['value'] + member_count = channel['num_members'] + slack_id = channel['id'] + name = channel['name'] + new_channel = Channel.new(topic: topic, member_count: member_count, slack_id: slack_id, name: name) + @@all << new_channel + end + return @@all + end + end +end \ No newline at end of file diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..d62a49f8 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,43 @@ +module SlackAPI + class Recipient + + attr_reader :slack_id, :name + + def initialize(slack_id:, name:) + @slack_id = slack_id + @name = name + end + + def send_message(text:) + body = { + text: text, + channel: slack_id, + token: ENV["TOKEN"], + } + + response = HTTParty.post("https://slack.com/api/chat.postMessage", + body: body, + headers: { "Content-Type" => "application/x-www-form-urlencoded" }) + + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, "Error when posting #{text} to #{name}, error: #{response.parsed_response["error"]}" + end + + return response.code == 200 && response.parsed_response["ok"] + end + + def self.load + raise NotImplementedError + end + + def details + raise NotImplementedError + end + + def self.list + raise NotImplementedError + end + + end + +end \ No newline at end of file diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..911b683e 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,10 +1,85 @@ #!/usr/bin/env ruby +require 'dotenv' +require 'httparty' +require_relative 'channel' +require_relative 'recipient' +require_relative 'user' +require_relative 'workspace' +Dotenv.load + +token = ENV['TOKEN'] def main - puts "Welcome to the Ada Slack CLI!" + input = 0 + + users = SlackAPI::User.load + channels = SlackAPI::Channel.load + workspace = SlackAPI::Workspace.new(users: users, channels: channels) + + until input == "G" do + puts "Welcome to the Ada Slack CLI!" + puts "What would you like to do?" + puts "[A] List users" + puts "[B] List channels" + puts "[C] Select user" + puts "[D] Select channel" + puts "[E] Show details of selected user / channel" + puts "[F] Send message" + puts "[G] Quit" + input = gets.chomp.upcase + valid_input = ["A", "B", "C", "D", "E", "F", "G"] - # TODO project + until valid_input.include?(input) + puts "Please try again" + input = gets.chomp.upcase + end + case input + when "A" + tp SlackAPI::User.list + when "B" + tp SlackAPI::Channel.list + when "C" + puts "Please supply a slack ID or user name (case sensitive!)" + workspace.selected = nil + user_id_or_name = gets.chomp + selected = workspace.select_user(id_or_name: user_id_or_name) + if selected + puts "Selected user: #{user_id_or_name}" + else + puts "User not found" + end + when "D" + puts "Please supply a slack ID or channel name (case sensitive!)" + workspace.selected = nil + chan_id_or_name = gets.chomp.downcase + workspace.select_channel(id_or_name: chan_id_or_name) + if selected + puts "Selected channel: #{chan_id_or_name}" + else + puts "Channel not found" + end + + when "E" + if workspace.selected + puts workspace.show_details + else + puts "No recipient selected \n\n" + end + when "F" + if workspace.selected + puts "Please type the message you want to send" + user_text = gets.chomp + if user_text != "" + workspace.send_message(text: user_text) + else + "Message cannot be empty" + end + else + puts "No recipient selected \n\n" + end + end + end puts "Thank you for using the Ada Slack CLI" end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 00000000..092f0d8d --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,49 @@ +require "dotenv" +require "httparty" +require "table_print" +require_relative "recipient" + +Dotenv.load + +module SlackAPI + class SlackApiError < StandardError; end + + class User < Recipient + + attr_reader :real_name + + BASE_URL = "https://slack.com/api" + USERS_LIST_PATH = "/users.list" + CHAT_POST_MESSAGE_PATH = "/chat.postMessage" + TOKEN = ENV["TOKEN"] + @@all = [] + + def initialize(real_name:, slack_id:, name:) + super(slack_id: slack_id, name: name) + @real_name = real_name + end + + def details + return "Name: #{name} + Slack ID: #{slack_id} + Real name: #{real_name}" + end + + def self.list + return @@all + end + + def self.load + query_parameters = { token: TOKEN } + response = HTTParty.get(BASE_URL << USERS_LIST_PATH, query: query_parameters) + response["members"].length.times do |i| + real_name = response["members"][i]["real_name"] + slack_id = response["members"][i]["id"] + name = response["members"][i]["name"] + new_user = SlackAPI::User.new(real_name: real_name, slack_id: slack_id, name: name) + @@all.push(new_user) + end + return @@all + end + end +end diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..08ee92d4 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,48 @@ +require 'dotenv' +require 'httparty' +require 'pry' +Dotenv.load + +module SlackAPI + class Workspace + + attr_reader :users, :channels + attr_accessor :selected + + def initialize(users:, channels:, selected: nil) + @users = users + @channels = channels + @selected = selected + end + + def select_channel(id_or_name:) + @channels.each do |channel| + if channel.name == id_or_name + @selected = channel + elsif channel.slack_id == id_or_name + @selected = channel + end + end + return @selected + end + + def select_user(id_or_name:) + @users.each do |user| + if user.name == id_or_name + @selected = user + elsif user.slack_id == id_or_name + @selected = user + end + end + return @selected + end + + def show_details + @selected.details + end + + def send_message(text:) + @selected.send_message(text: text) + end + end +end \ No newline at end of file diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..b44bc343 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,40 @@ +require 'minitest' +require_relative 'test_helper.rb' + +describe "Channel" do + before do + VCR.use_cassette("channel_list") do + @response = SlackAPI::Channel.load + end + end + + describe "details" do + + end + + describe "self.list" do + it "can list details of all the channels" do + expect(SlackAPI::Channel.list).must_be_kind_of Array + end + end + + describe "self.load" do + it "can find the list of channels" do + expect(@response).must_be_kind_of Array + expect(@response[0]).must_be_kind_of SlackAPI::Channel + expect(@response[0].name).must_equal "everyone" + expect(@response[0].slack_id).must_equal "CH2SBU69Y" + expect(@response[0].topic).must_equal "Company-wide announcements and work-based matters" + expect(@response[0].member_count).must_equal 2 + end + end + + it "sends a message to selected channel" do + VCR.use_cassette("channel_send_message") do + channel = SlackAPI::Channel.list[0] + test2 = channel.send_message(text: "hello") + expect(test2).must_equal true + end + end + +end \ No newline at end of file diff --git a/specs/recipient_spec.rb b/specs/recipient_spec.rb new file mode 100644 index 00000000..74ff73a3 --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1,20 @@ +require 'minitest' +require_relative 'test_helper.rb' + +describe "Recipient" do + before do + + end + + describe "self.get" do + + end + + describe "details" do + + end + + describe "self.list" do + + end +end \ No newline at end of file diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..2d5ca445 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -7,9 +7,23 @@ require 'minitest/skip_dsl' require 'vcr' +require_relative "../lib/user.rb" +require_relative "../lib/channel.rb" +require_relative "../lib/workspace.rb" +require_relative "../lib/recipient.rb" +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 + 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 Slack token lying around in a cassette file. + config.filter_sensitive_data("") do + ENV["TOKEN"] + end end \ No newline at end of file diff --git a/specs/user_spec.rb b/specs/user_spec.rb new file mode 100644 index 00000000..f1d25ce2 --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,39 @@ +require 'minitest' +require_relative 'test_helper.rb' + +describe "User" do + before do + VCR.use_cassette("load_users") do + @response = SlackAPI::User.load + end + end + + describe "self.load" do + it "can find the list of users" do + expect(@response).must_be_kind_of Array + expect(@response[0]).must_be_kind_of SlackAPI::User + expect(@response[0].name).must_equal "slackbot" + expect(@response[0].real_name).must_equal "Slackbot" + expect(@response[0].slack_id).must_equal "USLACKBOT" + end + end + + describe "self.list" do + it "prints out a table of users" do + expect(SlackAPI::User.list).must_be_kind_of Array + end + end + + describe "send message" do + it "sends a message to selected user" do + VCR.use_cassette("user_send_message") do + user = SlackAPI::User.list[0] + test = user.send_message(text: "hello") + expect(test).must_equal true + end + end + + end +end + + diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..f0f1a737 --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,127 @@ +require 'minitest' +require_relative 'test_helper.rb' + +describe "Workspace" do + before do + VCR.use_cassette("load_workspace") do + response = SlackAPI::User.load + response2 = SlackAPI::Channel.load + @workspace = SlackAPI::Workspace.new(users: response, channels: response2) + end + end + + describe "select_user" do + it "selects a user when name is provided" do + @workspace.select_user(id_or_name:"slackbot") + expect(@workspace.selected).must_be_kind_of SlackAPI::User + expect(@workspace.selected.name).must_equal "slackbot" + end + + it "selects a user when id is provided" do + @workspace.select_user(id_or_name:"USLACKBOT") + expect(@workspace.selected).must_be_kind_of SlackAPI::User + expect(@workspace.selected.slack_id).must_equal "USLACKBOT" + end + end + + describe "select_channel" do + it "returns nil if no user / channel is selected" do + expect(@workspace.selected).must_be_nil + end + + it "selects a channel when id is provided" do + @workspace.select_channel(id_or_name:"CH2SBU69Y") + expect(@workspace.selected).must_be_kind_of SlackAPI::Channel + expect(@workspace.selected.slack_id).must_equal "CH2SBU69Y" + end + + it "selects a channel when name is provided" do + @workspace.select_channel(id_or_name:"everyone") + expect(@workspace.selected).must_be_kind_of SlackAPI::Channel + expect(@workspace.selected.name).must_equal "everyone" + end + + it "returns nil if channel is not found" do + @workspace.select_channel(id_or_name:"madeup_channel") + expect(@workspace.selected).must_be_nil + end + end + + describe "show_details" do + it "shows details for a selected user" do + @workspace.select_user(id_or_name:"slackbot") + expect(@workspace.show_details).must_be_kind_of String + end + + it "shows details for a selected channel" do + @workspace.select_channel(id_or_name:"everyone") + expect(@workspace.show_details).must_be_kind_of String + end + + end + + describe "send_message" do + + it "will raise an error when given an invalid channel" do + bad_channel = SlackAPI::Channel.new(slack_id: "123", name: "bad", topic: "bad", member_count: "1") + @workspace.channels.push(bad_channel) + @workspace.select_channel(id_or_name:"bad") + VCR.use_cassette("send_message_bad_channel") do + exception = expect { + @workspace.send_message(text:"This post should not work") + }.must_raise SlackAPI::SlackApiError + end + @workspace.channels.pop + end + + it "will raise an error when given an invalid user" do + bad_user = SlackAPI::User.new(real_name: "baddie", slack_id: "123", name: "baddie") + @workspace.users.push(bad_user) + @workspace.select_user(id_or_name:"baddie") + VCR.use_cassette("send_message_bad_user") do + exception = expect { + @workspace.send_message(text:"This post should not work") + }.must_raise SlackAPI::SlackApiError + end + @workspace.users.pop + end + + it "will send a message to a channel" do + channel = @workspace.channels[0].name + @workspace.select_channel(id_or_name: channel) + VCR.use_cassette("send_message_to_channel") do + response = @workspace.send_message(text:"message to channel!") + expect(response).must_equal true + end + end + + it "will send a message to a user" do + user = @workspace.users[0].name + @workspace.select_user(id_or_name: user) + VCR.use_cassette("send_message_to_user") do + response = @workspace.send_message(text:"message to user!") + expect(response).must_equal true + end + end + + it "will send rasie an error for empty text" do + user = @workspace.users[0].name + @workspace.select_user(id_or_name: user) + VCR.use_cassette("send_empty_message_user") do + exception = expect { + @workspace.send_message(text:"") + }.must_raise SlackAPI::SlackApiError + end + end + + it "it sends message with digits for text" do + channel = @workspace.channels[0].name + @workspace.select_channel(id_or_name: channel) + VCR.use_cassette("send_digit_message_to_channel") do + response = @workspace.send_message(text: 123) + expect(response).must_equal true + end + end + + end +end \ No newline at end of file