Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
959f938
initial setup
angethuy Mar 13, 2020
d3ee1ce
added workspace class
angethuy Mar 13, 2020
9e33832
scaffolding for tests
angethuy Mar 16, 2020
196822a
basic command loop for CLI
angethuy Mar 16, 2020
0498001
workspace scaffold: requires, methods, NoRecepientError
angethuy Mar 16, 2020
9d78303
supporting classes scaffold
angethuy Mar 16, 2020
b726618
scaffolding cleanup
angethuy Mar 16, 2020
88ff398
pseudocode for tests
angethuy Mar 16, 2020
d1c1577
pseudocode for classes
angethuy Mar 16, 2020
995cccd
workspace tests written, 9/10 fail
angethuy Mar 17, 2020
e480f3c
added User init method tests and code
angethuy Mar 17, 2020
9608358
swapped Channels out for Conversations
angethuy Mar 17, 2020
4143796
user_test for methods init and get_all passing
angethuy Mar 17, 2020
c3138f1
all tests and methods for user passing
angethuy Mar 17, 2020
429d1bc
fixed bug caused by trying to process deleted users
angethuy Mar 17, 2020
6573a1b
upgraded CLI to prompt for input until user asks to exit
angethuy Mar 17, 2020
7567b9f
removed overlapping tests in workspace
angethuy Mar 17, 2020
3657bfb
channel tests filled out
angethuy Mar 24, 2020
4488f9d
filled out tests for direct messages
angethuy Mar 24, 2020
4c8dd5b
changed user tests
angethuy Mar 24, 2020
c61e5d1
new bad_reponse class
angethuy Mar 24, 2020
c97c786
channels now working
angethuy Mar 24, 2020
3c807d9
direct messages tests passing
angethuy Mar 24, 2020
dfad56e
modified CLI driver, cleaned up spacing
angethuy Mar 24, 2020
b9239d9
select user/channel working
angethuy Mar 25, 2020
3fe9b89
details and posting messages now working
angethuy Mar 25, 2020
5f75a98
added error handling for bad user input
angethuy Mar 27, 2020
d0f2b70
cleaned up spacing
angethuy Mar 27, 2020
3989252
fixed a bug where the recovery prompt wasn't looping correctly
angethuy Mar 27, 2020
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
7 changes: 7 additions & 0 deletions lib/bad_response_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Slack
class BadResponseError < StandardError
def initialize(msg="Slack API endpoint is NOT OK.")
super
end
end
end
37 changes: 37 additions & 0 deletions lib/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require_relative "conversation.rb"

module Slack
class Channel < Conversation
attr_reader :name, :topic, :member_count

def initialize(channel)
raise ArgumentError, "Trying to create Channel object with bad data: #{channel}." if channel["id"] == nil || !(channel["is_channel"])
super(channel)
@name = channel["name"]
@topic = channel["topic"]["value"]
@member_count = channel["num_members"]
end

def details
return "Details for this channel... \n name: #{name} \n topic: #{topic} \n number of members: #{member_count}"
end


# Method takes raw Channel data and converts it into an array of Channel objects.
def self.list_all
channels = get_all.map { |channel| Channel.new(channel)}
end

private

# Method uses http get to retrieve all Channel "objects"
# returns an array of Channels
def self.get_all
query = {
token: SLACK_TOKEN,
}
return super(query)["channels"]
end

end
end
43 changes: 43 additions & 0 deletions lib/conversation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require_relative "bad_response_error"

module Slack
class Conversation
attr_reader :id

def initialize(data)
@id = data["id"] #template
end

# Super method to send messages to selected conversation.
def post_message(message)
# API ENDPOINT: https://slack.com/api/chat.postMessage
# query: @id
results = HTTParty.post("https://slack.com/api/chat.postMessage", query: { token: SLACK_TOKEN, channel: id, text: message})
raise BadResponseError, "chat.postMessage endpoint response IS NOT OK." unless results["ok"]
end

# Placeholder method to be defined in child classes.
# Method will return a bunch of details about specified conversation
# Should probably implement in child classes so we can control the description to be user-friendly
# but if we're ok with a generic details printout, we can do it at parent Conversation level too
def details
raise NotImplementedError, "Define DETAILS method in child class."
end


# CLASS METHODS

def self.list_all
# Extend this method in child classes.
end

private

def self.get_all(query)
data = HTTParty.get("https://slack.com/api/conversations.list?", query: query)
raise BadResponseError, "Conversations.list endpoint response IS NOT OK." unless data["ok"]
return data
end

end
end
31 changes: 31 additions & 0 deletions lib/direct_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require_relative "conversation"

module Slack
class DirectMessage < Conversation
attr_reader :user

def initialize(data)
super(data)
@user = data["user"]
end


# CLASS METHODS
# Method takes raw Channel data and converts it into an array of Channel objects.
def self.list_all
direct_messages = get_all.map { |data| DirectMessage.new(data)}
end

private

# Method uses http get to retrieve all Channel "objects"
# returns an array of Channels
def self.get_all
query = {
token: SLACK_TOKEN,
types: "im",
}
return super(query)["channels"] #Slack considers direct messages to also be "channels"
end
end
end
119 changes: 115 additions & 4 deletions lib/slack.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,123 @@
#!/usr/bin/env ruby
require_relative 'workspace'
require "table_print"

OPTIONS = [
["1", "list users"],
["2", "list channels"],
["3", "select user"],
["4", "select a channel"],
["5", "show details"],
["6", "post a message"],
["7", "quit"],
]

# main loop of the CLI program
def main
puts "Welcome to the Ada Slack CLI!"
workspace = Workspace.new
@workspace = Slack::Workspace.new

choice = get_user_input
until (OPTIONS[-1].include? choice) #checks for quit command from user
perform_action(choice)
choice = get_user_input
end
puts "\n>>>>>> Thank you for using the Slack CLI! Goodbye."
end

# prompts for valid commands from user, then executes the command
def perform_action(choice)

until OPTIONS.any? { |option| option.include? choice } #validates user input
print "'#{choice}' is an invalid option, try again. > "
choice = gets.strip.downcase
end

case choice
when *OPTIONS[0]
puts "\n\n>>>>>>> LIST OF USERS"
count = 0
my_proc = Proc.new{count = count + 1}
tp @workspace.users, {user: lambda{ |u| my_proc.call }}, :id, :user_name, :real_name => {:display_method => :name}

when *OPTIONS[1]
puts "\n\n>>>>>>> LIST OF CHANNELS"
# tp @workspace.channels, :include => :id
count = 0
my_proc = Proc.new{count = count + 1}
tp @workspace.channels, {channel: lambda{ |u| my_proc.call }}, :id, :name, :topic, :member_count

when *OPTIONS[2]
make_selection("user", @workspace.users)

when *OPTIONS[3]
make_selection("channel", @workspace.channels)

when *OPTIONS[4] #details
if @workspace.selected.nil?
puts "ERROR: Oops, you haven't made a selection yet. Make a selection first."
else
puts @workspace.selected.details
end

when *OPTIONS[5] #post message
if @workspace.selected.nil?
puts "ERROR: Who ya trying to send a message to? Pick someone first, silly."
else
print "Enter the message that you want to send to #{@workspace.selected.name} > "
message = gets
@workspace.selected.post_message(message)
end
end

end

# presents the main menu and grabs user input
def get_user_input
puts "\n\nMAIN MENU - please select from the following"

OPTIONS.each do |option|
puts option.join(" ")
end

print "\nWhat would you like to do? > "
choice = gets.strip.downcase
return choice
end

# gets user input when they're selecting a user or channel
def make_selection(type, list)
puts "\n\n>>>>>>> SELECTING A #{type.upcase}"
print "Please enter the #{type} number or the ID as listed > "
input = gets.strip

# TODO project
if input != 0 && input.to_i == 0 #gave us an ID
puts "checking id #{input}..."
begin
@workspace.select_by_id(type, input)
rescue ArgumentError
puts "No #{type} with that ID exists."
else
puts "Selected #{type}: #{@workspace.selected.name}"
end
else #gave us an integer
puts "checking for user ##{input}..."
while input == 0 || input.to_i > list.length
input = validate_selection(input)
end
@workspace.select_by_index(type, input.to_i-1)
puts "Selected #{type}: #{@workspace.selected.name}"
end
end

puts "Thank you for using the Ada Slack CLI"
# helper method to validate
def validate_selection(input)
print "#{input} is not a valid choice, re-enter the number or ID > "
return gets.strip
end

main if __FILE__ == $PROGRAM_NAME

main if __FILE__ == $PROGRAM_NAME



50 changes: 50 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require "dotenv"
require "httparty"

Dotenv.load

SLACK_TOKEN = ENV["SLACK_TOKEN"]

module Slack
class User
attr_reader :id, :user_name, :name

def initialize(member)
raise ArgumentError, "Trying to create User object with bad data: #{member}." if member["id"] == nil || member["name"] == nil || member["real_name"] == nil
@id = member["id"]
@user_name = member["name"]
@name = member["real_name"]
end

def details
return "Details for this user: \n id: #{id} \n user name: #{user_name} \n real name: #{name}"
end

def post_message(message)
# API ENDPOINT: https://slack.com/api/chat.postMessage
# query: @id
results = HTTParty.post("https://slack.com/api/chat.postMessage", query: { token: SLACK_TOKEN, channel: id, text: message})
raise BadResponseError, "chat.postMessage endpoint response IS NOT OK." unless results["ok"]
end

# CLASS METHODS

# Parameter users: collection representing Users
# Returns an array of User objects
def self.list_all
members_including_deleted = get_all
members = members_including_deleted.reject { |member| member["deleted"] } #cover a wonky case
members.map { |member| User.new(member) }
end

private

# Method uses http get to retrieve all User "objects"
# returns an httparty Response object
def self.get_all
data = HTTParty.get("https://slack.com/api/users.list?", query: { token: SLACK_TOKEN, })
raise BadResponseError, "Users.list endpoint response IS NOT OK." unless data["ok"]
return data["members"]
end
end
end
70 changes: 70 additions & 0 deletions lib/workspace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require "dotenv"
require "httparty"
require_relative "direct_message"
require_relative "channel"
require_relative "user"
require_relative "bad_response_error"

module Slack
class Workspace
# attr_reader :users, :conversations, :selected
attr_reader :users, :channels, :selected

def initialize
@users = User.list_all
@channels = Channel.list_all
@direct_messages = DirectMessage.list_all
@selected = nil
end

# Method selects a user or channel using the name or slack ID.
def select_by_id(type, id)
result = nil

case type
when "channel"
result = @channels.index { |channel| channel.id == id }
raise ArgumentError, "no channel with id: #{id}" if result.nil?
@selected = @channels[result]
when "user"
result = @users.index { |user| user.id == id }
raise ArgumentError, "no user with id: #{id}" if result.nil?
@selected = @users[result]
end
end

def select_by_index(type, index)
case type
when "channel"
@selected = @channels[index]
when "user"
@selected = @users[index]
end
end

# Method shows details of the currently selected conversation.
def show_details
raise InvalidRecipientError, "No selection made yet. User must have a selected conversation."
end

# Method posts a message to the currently selected conversation.
def post_message
raise InvalidRecipientError, "No target conversation specified. Cannot send message."
end

private

def find_id
return

# check users and conversations for valid target
end

end

class InvalidRecipientError < StandardError
def initialize(msg="No valid user or conversation.")
super
end
end
end
Loading