diff --git a/.gitignore b/.gitignore index 8d6a243f..f1fc2633 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ build/ build-iPhoneOS/ build-iPhoneSimulator/ + ## Specific to RubyMotion (use of CocoaPods): # # We recommend against adding the Pods directory to your .gitignore. However diff --git a/lib/.keep b/lib/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/bot-settings.json b/lib/bot-settings.json new file mode 100644 index 00000000..7eed9135 --- /dev/null +++ b/lib/bot-settings.json @@ -0,0 +1 @@ +{"username":"Turtle","icon_emoji":":turtle:"} \ No newline at end of file diff --git a/lib/channel.rb b/lib/channel.rb new file mode 100644 index 00000000..2e223f56 --- /dev/null +++ b/lib/channel.rb @@ -0,0 +1,40 @@ +require "pry" + +require_relative "recipient" + +class Channel < Recipient + attr_reader :topic, :member_count + + def initialize(slack_id, name, topic, member_count) + super(slack_id, name) + @topic = topic + @member_count = member_count + end + + def details + return "#{name} #{topic} member count: #{member_count} slack id: #{slack_id}" + end + + def self.list + raw_data = self.get("channel") + + unless raw_data.code == 200 + raise SlackApiError, "Improper request: #{raw_data.message}" + end + channel_list = [] + channels = raw_data["channels"] + channels.each do |channel| + slack_id = channel["id"] + name = channel["name"] + topic = channel["topic"]["value"] + member_count = channel["members"].count + + new_channel = Channel.new(slack_id, name, topic, member_count) + channel_list << new_channel + end + return channel_list + end +end + +# binding.pry +# self.list diff --git a/lib/recipient.rb b/lib/recipient.rb new file mode 100644 index 00000000..2ffa1121 --- /dev/null +++ b/lib/recipient.rb @@ -0,0 +1,67 @@ +require "dotenv" +require "httparty" +require "json" +Dotenv.load + +class Recipient + class SlackApiError < StandardError; end + + attr_accessor :slack_id, :name + + def initialize(slack_id, name) + @slack_id = slack_id + @name = name + end + + CHANNEL_URL = "https://slack.com/api/channels.list" + USER_URL = "https://slack.com/api/users.list" + POST_URL = "https://slack.com/api/chat.postMessage" + def self.get(type) + params = { + "token" => ENV["SLACK_API_TOKEN"], + } + if type == "user" + url = USER_URL + elsif type == "channel" + url = CHANNEL_URL + end + + response = HTTParty.get(url, query: params) + end + + def send_msg(message, recipient) + # settings = {} + file = File.read("lib/bot-settings.json") + settings = JSON.parse(file) + params = { + "token" => ENV["SLACK_API_TOKEN"], + "channel" => recipient.slack_id, + "text" => message, + "username" => settings["username"], + "icon_emoji" => settings["icon_emoji"], + } + if recipient.class == User + params["as_user"] = true + end + + response = HTTParty.post( + POST_URL, + body: params, + headers: { "Content-Type" => "application/x-www-form-urlencoded" }, + ) + unless response.code == 200 && response.parsed_response["ok"] + raise SlackApiError, "Error: #{response.parsed_response["error"]}" + end + return response + end + + private + + def details + raise NotImplementedError, "Implement this in the child class." + end + + def self.list + raise NotImplementedError, "Implement this in the child class." + end +end diff --git a/lib/slack.rb b/lib/slack.rb index 960cf2f7..813348cf 100755 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -1,11 +1,60 @@ #!/usr/bin/env ruby +require_relative "workspace" def main puts "Welcome to the Ada Slack CLI!" - # TODO project + workspace = Workspace.new + input = "" + while input != "quit" + puts "Choose an option: \n list users \n list channels \n \n select user \n select channel \n details \n \n send message \n \n change settings \n quit" + input = gets.chomp + case input + when "list users" + puts workspace.print_details("users") + when "list channels" + puts workspace.print_details("channels") + when "select user" + print "Enter the user name or Slack ID: " + input_user = gets.chomp + workspace.select_user(input_user) + if workspace.selected == nil + puts "User not found" + end + when "select channel" + print "Enter the channel name or Slack ID: " + input_channel = gets.chomp + workspace.select_channel(input_channel) + if workspace.selected == nil + puts "Channel not found" + end + when "details" + if workspace.selected == nil + puts "Please select a user or channel as a recipient" + else + puts workspace.show_details + end + when "send message" + if workspace.selected == nil + puts "Please select a user or channel as a recipient" + else + print "Enter your message: " + text = gets.chomp + workspace.send_message(text) + end + when "change settings" + print "Enter new username: " + name = gets.chomp + print "Enter new emoji: " + emoji = gets.chomp + workspace.update_settings(name, emoji) + when "quit" + else + puts "Please select one of the options" + end + 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..a06c835a --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,43 @@ +require "pry" + +require_relative "recipient" + +class User < Recipient + attr_reader :real_name, :status_text, :status_emoji + + def initialize(slack_id, name, real_name, status_text, status_emoji) + super(slack_id, name) + @real_name = real_name + @status_text = status_text + @status_emoji = status_emoji + end + + def details + return "#{name} (#{real_name}) slack id: #{slack_id}" + end + + def self.list + raw_data = self.get("user") + + unless raw_data.code == 200 + raise SlackApiError, "Improper request: #{raw_data.message}" + end + + user_list = [] + members = raw_data["members"] + members.each do |member| + slack_id = member["id"] + name = member["name"] + real_name = member["real_name"] + status_text = member["status_text"] + status_emoji = member["status_emoji"] + + user = User.new(slack_id, name, real_name, status_text, status_emoji) + user_list << user + end + return user_list + end +end + +# binding.pry +# self.list diff --git a/lib/workspace.rb b/lib/workspace.rb new file mode 100644 index 00000000..4e9be038 --- /dev/null +++ b/lib/workspace.rb @@ -0,0 +1,63 @@ +require "pry" +require "json" + +require_relative "user" +require_relative "channel" + +class Workspace + attr_reader :users, :channels, :selected + + def initialize + @users = User.list + @channels = Channel.list + @selected = nil + end + + def select_channel(user_input) + selected = channels.select do |channel| + channel.name == user_input || channel.slack_id == user_input + end + @selected = selected.first + end + + def select_user(user_input) + selected = users.select do |user| + user.name == user_input || user.slack_id == user_input + end + @selected = selected.first + end + + def show_details + return @selected.details + end + + def print_details(recipients) + if recipients == "users" + return_array = [] + users.each do |user| + return_array << user.details + end + elsif recipients == "channels" + return_array = [] + channels.each do |channel| + return_array << channel.details + end + end + return return_array + end + + def update_settings(name, emoji) + json_hash = { "username" => name, + "icon_emoji" => emoji } + + File.open("lib/bot-settings.json", "w") do |f| + f.write(json_hash.to_json) + end + end + + def send_message(text) + selected.send_msg(text, selected) + end +end + +# binding.pry diff --git a/specs/channel_spec.rb b/specs/channel_spec.rb new file mode 100644 index 00000000..b5c9c1b0 --- /dev/null +++ b/specs/channel_spec.rb @@ -0,0 +1,15 @@ +require_relative "test_helper" + +describe "Channel class" do + describe "self.list" do + it "can return all channels" do + VCR.use_cassette("slack_channel") do + response = Channel.get("channel") + + expect(response["channels"]).wont_be_nil + expect(response["channels"].first["name"]).must_equal "random" + expect(response["channels"].first["members"].count).must_equal 2 + end + end + end +end diff --git a/specs/recipient_spec.rb b/specs/recipient_spec.rb new file mode 100644 index 00000000..0ad635fe --- /dev/null +++ b/specs/recipient_spec.rb @@ -0,0 +1 @@ +require "test_helper" diff --git a/specs/test_helper.rb b/specs/test_helper.rb index 81ccd06b..60844ebf 100644 --- a/specs/test_helper.rb +++ b/specs/test_helper.rb @@ -1,15 +1,31 @@ -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 "dotenv" +Dotenv.load + +require_relative '../lib/channel.rb' +require_relative '../lib/user.rb' +require_relative '../lib/recipient.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.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["SLACK_API_TOKEN"] + end +end diff --git a/specs/user_spec.rb b/specs/user_spec.rb new file mode 100644 index 00000000..4f455494 --- /dev/null +++ b/specs/user_spec.rb @@ -0,0 +1,22 @@ +require_relative "test_helper" + +describe "User class" do + describe "self.list" do + it "can return all users" do + VCR.use_cassette("slack_user") do + response = User.get("user") + + expect(response["members"]).wont_be_nil + expect(response["members"].first["name"]).must_equal "slackbot" + expect(response["members"].first["id"]).must_equal "USLACKBOT" + end + end + + # it "raises an exception if response is invalid" do + # VCR.use_cassette("slack_user") do + # response = User.get("user") + # expect(response["ok"]).must_equal false + # end + # end + end +end diff --git a/specs/workspace_spec.rb b/specs/workspace_spec.rb new file mode 100644 index 00000000..0451e99a --- /dev/null +++ b/specs/workspace_spec.rb @@ -0,0 +1,101 @@ +require_relative "test_helper" + +describe "Workspace class" do + describe "initialize" do + it "creates a list of channels and users" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + expect(workspace.channels.first).must_be_kind_of Channel + + expect(workspace.users.first).must_be_kind_of User + end + end + end + + describe "instance methods" do + it "returns text from print_details" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + expect(workspace.print_details("users")).must_be_kind_of Array + + expect(workspace.print_details("channels")).must_be_kind_of Array + end + end + + it "returns a user object from select_user" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + expect(workspace.select_user("slackbot")).must_be_kind_of User + + expect(workspace.select_user("USLACKBOT")).must_be_kind_of User + end + end + + it "returns a channel object from select_channel" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + expect(workspace.select_channel("random")).must_be_kind_of Channel + + expect(workspace.select_channel("CH2RY8RQT")).must_be_kind_of Channel + end + end + + it "returns channel details from show_details" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + workspace.select_channel("random") + expect(workspace.show_details).must_be_kind_of String + workspace.select_channel("CH2RY8RQT") + expect(workspace.show_details).must_be_kind_of String + end + end + + it "returns user details from show_details" do + VCR.use_cassette("slack_workspace") do + workspace = Workspace.new + workspace.select_user("slackbot") + expect(workspace.show_details).must_be_kind_of String + workspace.select_user("USLACKBOT") + expect(workspace.show_details).must_be_kind_of String + end + end + end + + describe "post message to slack" do + it "creates sends a message to a recipient" do + VCR.use_cassette("slack-posts") do + workspace = Workspace.new + workspace.select_channel("random") + response = workspace.send_message("This post should work") + expect(response["ok"]).must_equal true + end + end + + it "raises an error for invalid channel" do + VCR.use_cassette("slack-posts") do + workspace = Workspace.new + workspace.select_channel("random") + workspace.selected.slack_id = "garbage" + expect { workspace.send_message("This post should not work") }.must_raise Recipient::SlackApiError + end + end + + it "raises an error for invalid user" do + VCR.use_cassette("slack-posts") do + workspace = Workspace.new + workspace.select_user("slackbot") + workspace.selected.slack_id = "garbage" + expect { workspace.send_message("This post should not work") }.must_raise Recipient::SlackApiError + end + end + + it "raises an error for invalid message" do + VCR.use_cassette("slack-posts") do + workspace = Workspace.new + workspace.select_user("slackbot") + workspace.selected.slack_id = "garbage" + expect { workspace.send_message("") }.must_raise Recipient::SlackApiError + end + end + end +end