Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
8073e93
Added API token
ranuseh Mar 19, 2019
f55599f
Add VCR configurations to test_helper
JansenMartin Mar 19, 2019
f241dc8
Added files and recipient class and method outlines
ranuseh Mar 19, 2019
dc4523d
Add recipient_spec.rb and a test for instantiation. Add attr_reader …
JansenMartin Mar 19, 2019
545af26
added method to get recipients in slack_main.rb
ranuseh Mar 19, 2019
928543c
Add base URLs to channel.rb and user.rb. Add self.get method to Reci…
JansenMartin Mar 19, 2019
b5e8c5e
Added methods outline to workspace, user and channel.
ranuseh Mar 19, 2019
4f53578
Add instantiation of Workspace in slack_main.rb
JansenMartin Mar 19, 2019
27e2850
Implemented self.list method in channels
ranuseh Mar 20, 2019
f244829
Revise self.list method, add tests
JansenMartin Mar 20, 2019
1a228f3
Add 'table_print' gem
JansenMartin Mar 20, 2019
447ed65
Write details method and test for Channel
JansenMartin Mar 20, 2019
4b0069e
Added helper method to channels
ranuseh Mar 20, 2019
3b72fc4
Add self.send_message method and test. All tests passing
JansenMartin Mar 20, 2019
923fc92
added arg error and test to channel class
ranuseh Mar 20, 2019
f4e0c2c
Add messages to main driver code
JansenMartin Mar 20, 2019
632e595
Add menu and 'list channels' functionality
JansenMartin Mar 21, 2019
578c529
Added list method to users and tests to pass
ranuseh Mar 21, 2019
9e5b393
Added send msg method to user
ranuseh Mar 21, 2019
1f2fe55
user send message test all passing
ranuseh Mar 21, 2019
e343c31
List users implemented in slack_main
ranuseh Mar 21, 2019
3f83f98
Outline of methods in user and channel to select user or channel
ranuseh Mar 21, 2019
21fe12f
added comments to new select methods
ranuseh Mar 21, 2019
306d877
Moved send message and error message to recipient class
ranuseh Mar 21, 2019
96f5c24
Move exception conditionals to 'send_message' inside Recipient; Clean…
JansenMartin Mar 21, 2019
2f9c7f2
Added super slack id and name to channel and user class
ranuseh Mar 21, 2019
9e40151
Make User inherit 'name' and 'slack_id' attributes from Recipient cla…
JansenMartin Mar 21, 2019
0c35596
Added implement me in child class in details method in recipient class:
ranuseh Mar 21, 2019
62fdb9a
Add workspace_spec; All tests passing
JansenMartin Mar 21, 2019
ed448bc
Added test to workspace for channel class
ranuseh Mar 21, 2019
0ad511d
Add test for 'select_channel' method
JansenMartin Mar 21, 2019
1c70bcf
Added tests for invalid inputs in user and channel select user metho…
ranuseh Mar 21, 2019
8216737
Refactor 'select_channel' and 'select_user' methods; Revise tests
JansenMartin Mar 21, 2019
1aa5782
Added select user and select channel to main
ranuseh Mar 22, 2019
b65e289
Merging
ranuseh Mar 22, 2019
11cfb26
Initialize workspace inside driver code
JansenMartin Mar 22, 2019
b0f92c5
Use Workspace instance for list-users and list-channels functionalities
JansenMartin Mar 22, 2019
0d7e212
Add select-channel and select-user functionalities
JansenMartin Mar 22, 2019
6f810c8
Add show-details functionality to main; Fill in 'details' method in W…
JansenMartin Mar 22, 2019
aa7f35e
Add 'describe' blocks to tests
JansenMartin Mar 22, 2019
18a295d
Add empty describe block for 'show_details' method
JansenMartin Mar 22, 2019
f576965
Clean up comments
JansenMartin Mar 22, 2019
0895c5d
Replace curly-brace syntax with do...end block in 'select_channel' an…
JansenMartin Mar 22, 2019
0cfe2ad
Change 'slack_id' variable to 'identifier'; change wording in tests f…
JansenMartin Mar 22, 2019
9822a15
Add test for 'select_channel'; Revise 'select_channel' so that it can…
JansenMartin Mar 22, 2019
6c74475
Add test for 'select_user'; Revise 'select_user' so that it can selec…
JansenMartin Mar 22, 2019
46629ca
Add test to ensure 'select_channel' and 'select_user' methods are sel…
JansenMartin Mar 22, 2019
9ee02fd
Change variable name in tests for 'select_user' and 'select_channel' …
JansenMartin Mar 22, 2019
3b1c82c
Make more specific prompts for selecting users and channels
JansenMartin Mar 22, 2019
e6f4a0e
Clean up comments
JansenMartin Mar 22, 2019
f5f1bdf
Move 'selected' attribute to attr_accessor in Workspace; Make main le…
JansenMartin Mar 22, 2019
23f1e05
formatted list user and channel to print to a table.
ranuseh Mar 22, 2019
6945ab0
Added details to show to channel and user classes
ranuseh Mar 22, 2019
e8da559
Added message to details selection if no user or channel was selected
ranuseh Mar 22, 2019
c6b126f
User and Channel details formatted to display as a table in slack main
ranuseh Mar 22, 2019
5cb81ee
update user list in main
ranuseh Mar 22, 2019
431fce8
Add details to list-users and list-channels functionalities
JansenMartin Mar 22, 2019
d858007
Added send message option to menu
ranuseh Mar 22, 2019
8937fb4
Filled in the method for show details and test
ranuseh Mar 22, 2019
29b409a
Wrote test for send message in both user and channel
ranuseh Mar 22, 2019
8ce2e94
Small formatting changes
ranuseh Mar 22, 2019
3050c10
Fill out 'send_message' method inside Workspace; Add test
JansenMartin Mar 22, 2019
a686a9e
Add edge case test for sending messages to users
JansenMartin Mar 22, 2019
9022bf6
Add headers to users and channels tables
JansenMartin Mar 22, 2019
ee7f654
Fix typos in user_spec; Add tests for edge cases in workspace_spec
JansenMartin 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
Binary file added .DS_Store
Binary file not shown.
14 changes: 14 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// 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",
"program": "${workspaceRoot}/lib/slack_main.rb"
}
]
}
50 changes: 50 additions & 0 deletions lib/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require_relative "recipient"
require "pry"
require "dotenv"
require "httparty"
require "table_print"

Dotenv.load

module Slack
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

BASE_URL = "https://slack.com/api/"
CHANNEL_URL = "https://slack.com/api/channels.list"

def details
return {
Name: @name.capitalize,
Slack_id: @slack_id,
Topic: @topic,
Member_count: @member_count
}
end

def self.channels_get
query_params = { token: ENV["SLACK_API_TOKEN"] }
return Slack::Channel.get(CHANNEL_URL, query_params)
end

Choose a reason for hiding this comment

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

Since you're already inside of Slack::Channel, you can just say return get(...) on line 33.


def self.list
response = Slack::Channel.channels_get
channels = response["channels"].map do |channel|
name = channel["name"]
slack_id = channel["id"]
topic = channel["topic"]["value"]
member_count = channel["members"].count

Slack::Channel.new(name: name, slack_id: slack_id, topic: topic, member_count: member_count)
end

return channels
end
end
end
46 changes: 46 additions & 0 deletions lib/recipient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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

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

def self.get(url, params)
return HTTParty.get(url, query: params)
end

Choose a reason for hiding this comment

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

I think the params argument for this method is always the same: a hash containing the slack token. Could you supply that here and remove the extra argument?

Also, you should be doing things like error handling here.

In general, every time you add another layer to a piece of functionality, you should take care of some part of the puzzle in that layer. In this case, Channel.list is in charge of turning a response into an array of Channel objects, Channel.channels_get knows the URL and the query parameters, but this method doesn't add anything new.


def send_message(message)
body = {
text: message,
channel: @slack_id,
token: ENV["SLACK_API_TOKEN"],
}

response = HTTParty.post("#{BASE_URL}/chat.postMessage",
body: body,
headers: { "Content-Type" => "application/x-www-form-urlencoded" })

unless response.code == 200 && 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.

Good error checking here.


return true
end

private

def details
raise NotImplementedError, "Implement me in a child class!"
end

def self.list
raise NotImplementedError, "Implement me in a child class!"
end
end
end
11 changes: 0 additions & 11 deletions lib/slack.rb

This file was deleted.

110 changes: 110 additions & 0 deletions lib/slack_main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env ruby

require "dotenv"
require "pry"
require "httparty"
require "terminal-table"

require_relative "recipient"
require_relative "channel"
require_relative "user"
require_relative "workspace"

Dotenv.load

CHANNEL_URL = "https://slack.com/api/channels.list"
USER_URL = "https://slack.com/api/users.list"

Choose a reason for hiding this comment

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

You have these constants defined a few places. In general, each constant should be defined once. This makes your code easier to change, and avoids the problem of what to do if they don't have the same value.


def ask(prompt)
print "#{prompt} > "
input = gets.chomp
return input
end

def menu
puts "\nMENU"
puts "1. List users"
puts "2. List channels"
puts "3. Select user"
puts "4. Select channel"
puts "5. Details (on selected user/channel)"
puts "6. Send message"
puts "7. Quit"

input = ask("What do you want to do?")
return input
end

def clear_selected(workspace)
workspace.selected = nil
end

Choose a reason for hiding this comment

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

I would say this is a demeter violation. Instead of saying to the Workspace "your selected is now nil", it would be cleaner to call a method to ask it to clear itself.

It might be that you could move this method completely into Workspace.


def main
puts "Welcome to the Ada Slack CLI!"

workspace = Slack::Workspace.new

puts "\nHere's how many channels were loaded: #{workspace.channels.length}"

puts "\nHere's how many users were loaded: #{workspace.users.length}"

selection = menu

until selection == "7"
if selection == "1"
puts "\nHere are the users:"
rows = []
workspace.users.each_with_index do |user, i|
rows << [i + 1, user.real_name, user.slack_id, user.name]
end
# :headings => ['Word', 'Number'], :rows => rows
table = Terminal::Table.new :headings => ["", "Real name", "slack id", "Name"], :rows => rows
puts table
elsif selection == "2"
puts "\nHere are the channels:"
rows = []
workspace.channels.each_with_index do |channel, i|
rows << [i + 1, channel.name, channel.topic, "#{channel.member_count} members", channel.slack_id]
end
table = Terminal::Table.new :headings => ["", "Name", "Topic", "Member Count", "Slack id"], :rows => rows
puts table
elsif selection == "3" || selection == "4"
clear_selected(workspace)
case selection
when "3"
identifier = ask("\nPlease enter an id or username")
workspace.select_user(identifier)
when "4"
identifier = ask("\nPlease enter an id or channel name")
workspace.select_channel(identifier)
end

unless workspace.selected.nil?
puts "You've selected: #{workspace.selected.name}"
else
puts "Whoops! User or channel not found. Please try again."
end
elsif selection == "5"
if workspace.selected.nil?
puts "Whoops! You must select a user or channel first"
else
puts "Here are some details on your selection:"
details = workspace.show_details
puts details
end
elsif selection == "6"
if workspace.selected.nil?
puts "Whoops! You must select a recipient first"
else
message = ask("Enter the message you want to send to #{workspace.selected.name}")
workspace.send_message(message)
end
else puts "\nInvalid entry. Please try again!" end

selection = menu
end

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

main
53 changes: 53 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require_relative "recipient"
require "pry"
require "dotenv"
require "httparty"
require "table_print"

Dotenv.load

module Slack
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

BASE_URL = "https://slack.com/api/"
USER_URL = "https://slack.com/api/users.list"

def details
return {
Name: @real_name,
Status: @status_text,
Emoji: @status_emoji,
Slack_id: @slack_id,
Username: @name
}
end

def self.users_get
query_params = { token: ENV["SLACK_API_TOKEN"] }
return Slack::User.get(USER_URL, query_params)
end

def self.list
response = Slack::User.users_get
users = response["members"].map 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"]

Slack::User.new(name: name, slack_id: slack_id, real_name: real_name, status_text: status_text, status_emoji: status_emoji)
end

return users
end
end
end
52 changes: 52 additions & 0 deletions lib/workspace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require "pry"
require "dotenv"
require "httparty"
require "table_print"
require "terminal-table"

require_relative "user"
require_relative "channel"

module Slack
class Workspace
attr_accessor :selected
attr_reader :users, :channels

def initialize
@users = Slack::User.list
@channels = Slack::Channel.list
@selected = nil
end

def select_channel(identifier)
@selected = @channels.find do |channel|
channel.slack_id == identifier || channel.name == identifier
end
end

def select_user(identifier)
@selected = @users.find do |user|
user.slack_id == identifier || user.name == identifier
end
end

def show_details
details = @selected.details

rows = []

Choose a reason for hiding this comment

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

Good use of polymorphism to keep this code generic, not specific to users or channels.

details.each do |key, details|
rows << [key, details]
end

table = Terminal::Table.new :rows => rows
return table
end

def send_message(input)
@selected.send_message(input)
# The user gives a message
# This method enters user's message as an argument for Recipient's send_message

end
end
end
52 changes: 52 additions & 0 deletions specs/channel_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'simplecov'
SimpleCov.start

require_relative 'test_helper'
require_relative '../lib/channel.rb'

describe 'Channel class' do
before do
VCR.use_cassette('channel_list') do
@response = Slack::Channel.list
end
end

it 'can retrieve a list of channels' do
expect(@response).must_be_kind_of Array
expect(@response[0]).must_be_kind_of Slack::Channel
expect(@response[0].member_count).must_be_kind_of Integer
end

it 'can retrieve details of a specific channel' do
expect (@response[0].details).must_be_kind_of Hash
end

describe 'send_message to channel' do
it 'can send a message fine' do
VCR.use_cassette('slack_posts_channel') do
selected_channel = @response[1]
response = selected_channel.send_message('This is our Test Post to channel!')
expect(response).must_equal true
end
end

it 'will raise an error when given an invalid channel' do
VCR.use_cassette('slack-posts_channel') do
@response << Slack::Channel.new(
slack_id: 'bogus',
name: 'bogus',
topic: 'bogus',
member_count: 'bogus'
)

bogus_selected_channel = @response.last

exception = expect {
bogus_selected_channel.send_message('This post should not work')
}.must_raise Slack::SlackApiError

expect(exception.message).must_equal 'channel_not_found'
end
end
end
end
Loading