Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .floo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"url": "https://floobits.com/christinaminh/slack-cli"
}
5 changes: 5 additions & 0 deletions .flooignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern
node_modules
tmp
vendor
.idea
1 change: 1 addition & 0 deletions bot_settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"icon_emoji":":sparkle:","username":"bot"}
34 changes: 34 additions & 0 deletions lib/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require_relative "recipient"

CONVERSATIONS_URL = "https://slack.com/api/conversations.list"

Choose a reason for hiding this comment

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

Good use of a constant!


class Channel < Recipient
attr_reader :id, :name, :topic, :member_count

def initialize(id:, name:, topic: nil, member_count: nil)
super(id, name)

@topic = topic
@member_count = member_count
end


def self.list_all
response = get(CONVERSATIONS_URL)

channels_list = response["channels"].map do |channel|
Channel.new(
id: channel["id"],
name: channel["name"],
topic: channel["topic"]["value"],
member_count: channel["num_members"]
)
end

return channels_list
end

def details
return "\nSlack ID: #{self.id}\nName: #{self.name}\nTopic: #{self.topic}\nMember count: #{self.member_count}"
end
end
58 changes: 58 additions & 0 deletions lib/recipient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'dotenv'
require 'json'

# require_relative '../lib/bot_settings.json'
Dotenv.load

class SlackApiError < StandardError; end
class NoMessageError < StandardError; end

class Recipient
attr_reader :id, :name

def initialize(id, name)
@id = id
@name = name
end

API_KEY = ENV["SLACK_API_TOKEN"]

Choose a reason for hiding this comment

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

The README said that this should be named SLACK_TOKEN:

Suggested change
API_KEY = ENV["SLACK_API_TOKEN"]
API_KEY = ENV["SLACK_TOKEN"]

def self.get(url)
response = HTTParty.get(url, query: { token: API_KEY })

raise SlackApiError.new(response["error"]) unless response.parsed_response["ok"]

return response
end

CHAT_URL = "https://slack.com/api/chat.postMessage"
BOT_API_KEY = ENV["SLACK_BOT_API_TOKEN"]

Choose a reason for hiding this comment

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

You should use a single token for getting and sending.

Suggested change
BOT_API_KEY = ENV["SLACK_BOT_API_TOKEN"]
BOT_API_KEY = ENV["SLACK_TOKEN"]

def send_message(message)
raise NoMessageError, "No message to send." if message.nil? || message.empty?

settings_json = File.read("bot_settings.json")
settings = JSON.parse(settings_json)

response = HTTParty.post(CHAT_URL,
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
body:{
token: BOT_API_KEY,
channel: self.id,
text: message,
icon_emoji: settings["icon_emoji"],
username: settings["username"]
})

raise SlackApiError, "Error occurred when sending #{message} to #{self.name}: #{response.parsed_response["error"]}" unless response.parsed_response["ok"]

return true
end

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

def self.list_all
raise NotImplementedError, 'Implement me in a child class!'
end
end

132 changes: 129 additions & 3 deletions lib/slack.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,138 @@
#!/usr/bin/env ruby
require 'httparty'
require 'table_print'
require 'json'

require_relative 'workspace'

def find(dataset, workspace)
print "Would you like to enter a name or id? "
input = gets.chomp.downcase

until input == "name"|| input == "id"
puts "Please enter 'name' or 'id': "
input = gets.chomp.downcase
end

if input == "name"
if dataset[0].instance_of? User
print "Enter username: "
elsif dataset[0].instance_of? Channel
print"Enter channel name: "
end

input = gets.chomp.downcase

return workspace.select(dataset: dataset ,name: input)

elsif input == "id"
print "Enter id: "
input = gets.chomp.upcase

return workspace.select(dataset: dataset ,id: input)
end

rescue ArgumentError => error_message
puts "Encountered an error: #{error_message}\n\n"
end

def message
print "Enter message => "
message = gets.chomp

if message.empty?
puts "Invalid message (no content). Please enter a VALID message or hit 'ENTER' to return to main menu. => "
message = gets.chomp.downcase
end

return message
end

def change_settings
settings_json = File.read("bot_settings.json")
settings = JSON.parse(settings_json)
Comment on lines +51 to +52

Choose a reason for hiding this comment

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

The bot settings file is a nice touch. 🤖


puts "Do you want to change username or emoji?"
input = gets.chomp.downcase

if input == "username"
print "Enter new username: "
new_setting = gets.chomp

settings["username"] = new_setting
File.write("bot_settings.json", settings.to_json)

elsif input == "emoji"
print "Enter new emoji: "
new_setting = gets.chomp

settings["icon_emoji"] = new_setting
File.write("bot_settings.json", settings.to_json)
end

end

def main
puts "Welcome to the Ada Slack CLI!"
workspace = Workspace.new

# TODO project
while true
puts "\nWhat would you like to do?
- LIST USERS
- LIST CHANNELS
- SELECT USER
- SELECT CHANNEL
- DETAILS
- SEND MESSAGE
- CHANGE SETTINGS
- QUIT"

print "\nPlease enter a request: "
input = gets.chomp.downcase

case input
when "list users"
tp workspace.users, "id", "name", "real_name"

when "list channels"
tp workspace.channels, "id", "name", "topic", "member_count"

when "select user"
find(workspace.users, workspace)
puts "You've selected '#{workspace.selected.name}'!" unless workspace.selected.nil?

when "select channel"
find(workspace.channels, workspace)
puts "You've selected '#{workspace.selected.name}'!" unless workspace.selected.nil?

when "details"
if workspace.selected
puts workspace.show_details
else
puts "No user or channel selected.\n"
end
when "change settings"
change_settings
when "send message"
begin
if workspace.selected
workspace.send_message(message)
else
puts "No user or channel selected.\n"
end
rescue NoMessageError
puts "No message entered."
rescue SlackApiError => error_message
puts "Encountered an error: #{error_message}\n\n"
end

when "quit"
puts "\n👋 👋 Thank you for using the Ada Slack CLI...exiting."
exit

puts "Thank you for using the Ada Slack CLI"
else
puts "Invalid choice."
end
end
end

main if __FILE__ == $PROGRAM_NAME
29 changes: 29 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require_relative "recipient"

USERS_URL = "https://slack.com/api/users.list"

class User < Recipient
attr_reader :id, :name, :real_name

def initialize(id:, name:, real_name: nil)
super(id, name)

@real_name = real_name
end


def self.list_all
response = get(USERS_URL)

users_list = response["members"].map do |user|
User.new(id: user["id"], name: user["name"], real_name: user["real_name"])
end

return users_list
end

def details
return "\nSlack ID: #{self.id}\nUsername: #{self.name}\nReal Name: #{self.real_name}"
end

end
36 changes: 36 additions & 0 deletions lib/workspace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require_relative 'user'
require_relative 'channel'


class Workspace
attr_reader :users, :channels, :selected

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

def select(dataset: , id: nil, name: nil)
@selected = dataset.find do |recipient|
recipient.id == id || recipient.name == name
end

raise ArgumentError.new ("User not found.") if @selected == nil && dataset == @users
raise ArgumentError.new ("Channel not found.") if @selected == nil && dataset == @channels

return @selected
end

def show_details
raise ArgumentError.new ("No user or channel selected.") unless @selected

Choose a reason for hiding this comment

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

I think I would prefer this to be something like a NoRecipientSelectedError instead of an ArgumentError since nothing is actually wrong with the arguments to this method.


return @selected.details
end

def send_message(message)
raise ArgumentError.new ("No user or channel selected.") unless @selected

return @selected.send_message(message)
end
end
Loading