Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3d87a07
checked that could connect to API and print out channel list
cyndilopez Mar 19, 2019
50d8f7a
set up test_helper
cyndilopez Mar 19, 2019
18580f9
created user class
MariaWissler Mar 19, 2019
d40ea59
created test or Recipient
MariaWissler Mar 19, 2019
2264e5b
created Reciepient class
MariaWissler Mar 19, 2019
041a674
api token variable changed
cyndilopez Mar 19, 2019
3f397b0
merged changes
cyndilopez Mar 19, 2019
e11022c
initialized recipient and tested instance of recipient
cyndilopez Mar 19, 2019
b6f7258
added get method and test for connectivity
cyndilopez Mar 19, 2019
66d4662
name connection
cyndilopez Mar 19, 2019
8f410b2
modified self.get and self.list
cyndilopez Mar 19, 2019
91ee437
Created test for self_list in User_spec , modified self_list in User
cyndilopez Mar 20, 2019
ecf4c11
added details method to be implemented in child class
cyndilopez Mar 20, 2019
697db06
option to quit added
cyndilopez Mar 20, 2019
a9ba71f
require relative channel
cyndilopez Mar 20, 2019
58b7b46
added tests to check count of users returned
cyndilopez Mar 20, 2019
efc62ed
initialized channel and self.list and tests for it
cyndilopez Mar 20, 2019
769c283
now calling super in channel, fixed tests for this
cyndilopez Mar 20, 2019
3287b9f
filled out details methods for user and channel. no tests
cyndilopez Mar 20, 2019
30297eb
refactored to make tests more general
cyndilopez Mar 21, 2019
d385356
apierror class
cyndilopez Mar 21, 2019
e8fd7fd
now raises error if the API call didn't work
cyndilopez Mar 21, 2019
2823311
test for error raised when method is unknown
cyndilopez Mar 21, 2019
264f5af
require relative apierror
cyndilopez Mar 21, 2019
fa89713
set up workspace class file and specs
cyndilopez Mar 21, 2019
4cabb7c
pseudocode cli
cyndilopez Mar 21, 2019
2f53d3b
created select_user and select_channel methods, implemented them in t…
cyndilopez Mar 21, 2019
a8c729c
corrected topic display
cyndilopez Mar 21, 2019
2423c69
added details option and options for incorrect user input
cyndilopez Mar 21, 2019
25304d6
created rescue methods so that a user or channel was required to be s…
cyndilopez Mar 22, 2019
d9442ea
added tests for send_message
cyndilopez Mar 22, 2019
f5a5011
formatting
cyndilopez Mar 22, 2019
d7b5afb
formatting
cyndilopez Mar 22, 2019
0a0deca
added ability to change settings and select bot username
cyndilopez Mar 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bot-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"username":"Maria Wissler"}
1 change: 1 addition & 0 deletions lib/apierror.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class SlackApiError < StandardError; end
32 changes: 32 additions & 0 deletions lib/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require "pry"
require "httparty"

class Channel < Recipient
attr_reader :topic, :member_count

def initialize(name:, slack_id:, topic:, member_count:)
super(name: name, slack_id: slack_id)
@topic = topic
@member_count = member_count
end

def self.list
response = self.get("channels.list")
channel_list = []
response["channels"].each do |channel|
name = channel["name"]
slack_id = channel["id"]
topic = channel["topic"]
member_count = channel["members"].count
channel_list << self.new(name: name, slack_id: slack_id, topic: topic, member_count: member_count)
end
return channel_list
end

def details
puts "Name: #{self.name}"
puts "ID: #{self.slack_id}"
puts "Topic: #{self.topic["value"]}"
puts "Number of members: #{self.member_count}"
end
end
45 changes: 45 additions & 0 deletions lib/recipient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require "pry"
require "httparty"

class Recipient
BASE_URL = "https://slack.com/api/"

attr_reader :slack_id, :name

def initialize(slack_id:, name:)
@slack_id = slack_id
raise ArgumentError if !name.is_a? String
@name = name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best practice is to avoid explicit type checking in a dynamic language like Ruby. This is for two reasons. First, if name really needs to be a String, then we'll get an error soon enough one way or the other. Second, if the user passes in something that close enough to a string to work (i.e. implements the String interface), there's no reason for our code to fail.

end

def send_message(params)
endpoint = "chat.postMessage"
url = BASE_URL + endpoint
params[:token] = ENV["SLACK_API_TOKEN"]
response = HTTParty.post(url, body: params)
unless response.code == 200 && response.parsed_response["ok"]
raise SlackApiError, response["error"]
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 21 raises an exception, but not the one you want.

select user
You chose to select a user. Please provide a username or Slack ID
dan
Traceback (most recent call last):
	5: from lib/slack.rb:105:in `<main>'
	4: from lib/slack.rb:40:in `main'
	3: from lib/slack.rb:40:in `new'
	2: from /Users/droberts/Ada/c11/projects/slack-cli/lib/workspace.rb:11:in `initialize'
	1: from /Users/droberts/Ada/c11/projects/slack-cli/lib/user.rb:16:in `list'
/Users/droberts/Ada/c11/projects/slack-cli/lib/recipient.rb:33:in `get': uninitialized constant Recipient::SlackApiError (NameError)

Ruby doesn't know what a SlackApiError is, so you get a NameError when you try to raise one. Looks like you're missing require_relative 'apierror' at the top of this file.

return response
end

private

def self.get(endpoint, params = {})
url = BASE_URL + endpoint
params[:token] = ENV["SLACK_API_TOKEN"]
response = HTTParty.get(url, query: params)
unless response.code == 200 && response.parsed_response["ok"]
raise SlackApiError, response["error"]
end
return response
end

def self.list
raise NotImplementedError, "Implement me in a child class!"
end

def self.details
raise NotImplementedError, "Implement me in a child class!"
end
end
101 changes: 99 additions & 2 deletions lib/slack.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,108 @@
#!/usr/bin/env ruby
require "pry"
require "httparty"
require "dotenv"
require_relative "../lib/workspace"
Dotenv.load

def display_options
puts "\nChoose from one of the following:"
puts "------------------------------"
puts "\nSelect user"
puts "\nSelect channel"
puts "\nDetails"
puts "\nMessage"
puts "\nChange Settings"
puts "\nQuit"
option = gets.chomp.downcase
return verify_options(option)
end

def verify_options(option)
options = ["select user", "select channel", "details", "message", "change settings", "quit"]
until options.include?(option)
puts "Please input a valid option."
option = display_options
end
return option
end

def main
puts "Welcome to the Ada Slack CLI!"
puts "\nWhat would you like to do?"

# TODO project
option = display_options
until option == "quit"
case option
when "select user"
puts "You chose to select a user. Please provide a username or Slack ID"
selected = gets.chomp()
workspace = Workspace.new(selected: selected)
recipient = workspace.select_user
until !recipient.nil?
puts "Please provide a valid username or Slack ID"
selected = gets.chomp
workspace = Workspace.new(selected: selected)
recipient = workspace.select_user
end
puts "You have selected #{recipient.real_name}"
puts "\nWhat would you like to do next?"
option = display_options
when "select channel"
puts "You chose to select a channel. Please provide a channel name or Slack ID"
selected = gets.chomp()
workspace = Workspace.new(selected: selected)
recipient = workspace.select_channel
until !recipient.nil?
puts "Please provide a valid Channel name or Slack ID"
selected = gets.chomp
workspace = Workspace.new(selected: selected)
recipient = workspace.select_channel
end
puts "You have selected #{recipient.name}"
option = display_options
when "details"
begin
workspace.show_details(recipient)
puts "\nWhat would you like to do next?"
option = display_options
rescue
puts "You must select a user or channel first."
puts "\nWhat would you like to do next?"
option = display_options
end
when "message"
begin
recipient.slack_id #checking if recipient exists; if it doesn't will throw name error => rescue clause
puts "What message would you like to send?"
message = gets.chomp
workspace.send_message(message, recipient)
puts "\nYou're message has been sent."
puts "\nWhat would you like to do next?"
option = display_options
rescue
puts "You must select a user or channel first."
puts "\nWhat would you like to do next?"
option = display_options
end
when "change settings"
puts "You can change the username displayed"
# puts "Please type either 'username' or 'icon emoji' or both to change either"
puts "What username would you like to use?"
setting_username_change = gets.chomp
params = {}
params[:username] = setting_username_change
Workspace.save_settings(params)
puts "Thanks, username is now #{setting_username_change}."
puts "Quit and restart the program for this change to be implemented"
option = display_options
end
end

puts "Thank you for using the Ada Slack CLI"
end

main if __FILE__ == $PROGRAM_NAME
main if __FILE__ == $PROGRAM_NAME

def verify_icon_emojis
end
35 changes: 35 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require "pry"
require "httparty"
require_relative "recipient.rb"

class User < Recipient
attr_reader :real_name, :status_text, :status_emoji

def initialize(name:, slack_id:, real_name:, status_text: nil, status_emoji: nil)
super(name: name, slack_id: slack_id)
@real_name = real_name
@status_text = status_text
@status_emoji = status_emoji
end

def self.list #factory method
response = self.get("users.list")
user_list = []
response["members"].each do |member|
name = member["name"]
slack_id = member["id"]
real_name = member["real_name"]
status_text = member["profile"]["status_text"]
status_emoji = member["profile"]["status_emoji"]
user_list << self.new(name: name, slack_id: slack_id, real_name: real_name, status_text: status_text, status_emoji: status_emoji)
end
return user_list
end

def details #business logic
puts "Username: #{self.name}"
puts "ID: #{self.slack_id}"
puts "Name: #{self.real_name}"
puts "Status: #{self.status_text}"
end
end
57 changes: 57 additions & 0 deletions lib/workspace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require "httparty"
require_relative "../lib/user"
require_relative "../lib/channel"
require_relative "../lib/recipient"
require "json"

class Workspace
attr_reader :users, :channels, :selected

def initialize(selected:)
@users = User.list
@channels = Channel.list
@selected = selected # either user or channel info
end

def select_user
user_selected = users.detect do |user|
user.slack_id == selected || user.name == selected
end
return user_selected

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good use of the .detect enumerable here.

Since this doesn't save the user in @selected, I'm confused what that instance variable is for.

end

def select_channel
channel_selected = channels.detect do |channel|
channel.slack_id == selected || channel.name == selected
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is very similar to the code for select_user above. Could you DRY this up somehow?

return channel_selected
end

def self.save_settings(params)
settings_file = File.open("bot-settings.json", "w") do |f|
f.write(params.to_json)
end
end

def bot_settings_file_exist
begin
readfile = File.read("bot-settings.json")
return readfile
rescue
readfile = nil
end
end

def send_message(message, recipient)
params = {}
params[:text] = message
params[:channel] = recipient.slack_id

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking the recipient as an argument here, you should send the message to whatever is saved in @selected. Similarly for details below.

readfile = bot_settings_file_exist #check if settings have been changed
params.merge!(eval(readfile)) if !readfile.nil?
recipient.send_message(params)
end

def show_details(recipient)
recipient.details
end
end
1 change: 1 addition & 0 deletions settings_slackapi.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"{:username=>""cyndi""}"
62 changes: 62 additions & 0 deletions specs/channel_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require_relative "test_helper"
require "pry"
describe "channel class" do
describe "initialize" do
it "creates and instance of channel" do
topic = "random"
member_count = 3
name = "cyndilopez6"
slack_id = 1
expect(Channel.new(name: name, slack_id: slack_id, topic: topic, member_count: member_count)).must_be_kind_of Channel
end
end

describe "can connect to API" do
it "accesses api" do
VCR.use_cassette("connect to endpoints channels_list") do
endpoint = "channels.list"
@response = Channel.get(endpoint)
end
expect(@response.code == 200 && @response.parsed_response["ok"]).must_equal true
end
end

describe "raises errors for incorrect endpoint" do
it "raises an error for incorrect endpoint" do
VCR.use_cassette("check_method_error_raised") do
endpoint = "ret424252E#1231+=.y"
exception = expect { Channel.get(endpoint) }.must_raise SlackApiError
expect(exception.message).must_equal "unknown_method"
end
end

# it "raises an error for incorrect token" do
# VCR.use_cassette("check_auth_error_raised") do
# endpoint = "channels.list"
# params = {:token => "0123456789abcdef"}
# p params
# expect(Channel.get(endpoint, params)).must_equal "invalid_auth"
# end
# end
end

describe "creates list of channels" do
it "returns a type of array" do
VCR.use_cassette("returns array") do
expect(Channel.list.is_a? Array).must_equal true
end
end

it "returns an array of Channel objects" do
VCR.use_cassette("returns object Channel") do
expect(Channel.list[0]).must_be_kind_of Channel
end
end

it "returns an accurate list of channels in slack workspace" do
VCR.use_cassette("correct channels") do
expect(Channel.list.map { |channel| channel.name }.length).must_be :>, 0
end
end
end
end
44 changes: 44 additions & 0 deletions specs/recipient_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require_relative "test_helper"
require "pry"
describe "recipient class" do
describe "initialize" do
it "creates an instance of Recipient" do
#check slack_if format
slack_id = 1
name = "Maria"
expect(Recipient.new(slack_id: slack_id, name: name)).must_be_kind_of Recipient
end

it "raises an argument error if name is not a string" do
expect { Recipient.new(slack_id: 1, name: 21) }.must_raise ArgumentError
end
end

describe "can connect to API" do
it "can connect" do
VCR.use_cassette("find channels") do
response = Recipient.get("channels.list")
expect(response["channels"]).wont_be_nil
expect(response["channels"].map { |channel| channel["name"] }.length).must_be :>, 0
end
end

it "gives a list with more than one user name" do
VCR.use_cassette("find channels") do
endpoint = "users.list"
response = Recipient.get(endpoint)
# Binding.pry
expect(response).wont_be_nil
expect(response["members"].map { |member| member["name"] }.length).must_be :>, 0
end
end
it "can find the status of a member" do
VCR.use_cassette("user status") do
endpoint = "users.list"
response = Recipient.get(endpoint)
expect(response["members"][0]["profile"]["status_text"].length).wont_be_nil
expect(response["members"].select { |member| member["real_name"] == "Maria Wissler" }[0]["profile"]["status_text"]).must_be_kind_of String
end
end
end
end
Loading