hulaki 0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,22 +1,18 @@
1
1
  class Hulaki::ContactParser
2
-
3
- DefaultFilePath = '~/hulaki/contact.csv'
2
+ DEFAULT = File.expand_path('~/hulaki/contact.csv')
3
+ @@default_file_path = DEFAULT
4
4
 
5
5
  def perform
6
- parse
7
- end
8
-
9
- private
10
- def parse
11
6
  options = {:strings_as_keys => true, :downcase_header => true}
12
- SmarterCSV.process(contact_file, options)
7
+ SmarterCSV.process(@@default_file_path, options)
13
8
  rescue Errno::ENOENT
14
- puts 'Contact file not found. Make sure there is file'
15
- puts @file_path
16
- nil
9
+ raise Hulaki::InvalidFilePath
17
10
  end
18
11
 
19
- def contact_file
20
- @file_path = File.expand_path(DefaultFilePath)
12
+ private
13
+ class << self
14
+ def default_file_path=(path)
15
+ @@default_file_path = File.expand_path(path)
16
+ end
21
17
  end
22
18
  end
@@ -0,0 +1,90 @@
1
+ require 'clipboard'
2
+
3
+ class Hulaki::Core
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def perform
9
+ if @config.search_keyword
10
+ handle_search_and_clipboard
11
+ else
12
+ handle_messaging
13
+ end
14
+ rescue CSV::MalformedCSVError
15
+ puts 'Your contact.csv file is invalid or has invalid/malformed UTF characters.'.red
16
+ puts 'Please use a valid CSV file.'.red
17
+ end
18
+
19
+ private
20
+ def handle_messaging
21
+ puts '~' * 100
22
+ puts 'Welcome to Hulaki : Your best companion! to make your day great.'
23
+ puts '~' * 100
24
+
25
+ # It supports multiple recipients like two phonenumbers separated by commas `,`
26
+ @config.to.each do |recipient|
27
+ if is_phone?(recipient)
28
+ handle_sms(recipient)
29
+ elsif is_email?(recipient)
30
+ handle_email(recipient)
31
+ end
32
+ end
33
+
34
+ puts '~' * 100
35
+ puts @config.message
36
+ puts '~' * 100
37
+ end
38
+
39
+ def is_phone?(number)
40
+ Hulaki::SmsValidator.is_phone_number?(number)
41
+ end
42
+
43
+ def is_email?(email)
44
+ Hulaki::EmailValidator.is_email?(email)
45
+ end
46
+
47
+ def handle_email(recipient)
48
+ puts "Sending email to #{recipient}"
49
+ email_handler = Hulaki::Mailer.new(
50
+ {
51
+ :to => recipient,
52
+ :message => @config.message,
53
+ :subject => @config.subject,
54
+ :from => @config.from
55
+ }).deliver
56
+ puts "Email sent to `#{recipient}`. from `#{get_sender}`"
57
+ end
58
+
59
+ def handle_sms(recipient)
60
+ puts "Sending SMS to #{recipient}"
61
+ sms_handler = Hulaki::SmsHandler.new(
62
+ {
63
+ :to => recipient,
64
+ :message => @config.message,
65
+ :gateway => @config.gateway,
66
+ :from => @config.from
67
+ })
68
+ if sms_handler.send
69
+ puts "SMS sent successfully to #{recipient} using `#{sms_handler.gateway.class}` gateway"
70
+ end
71
+ end
72
+
73
+ def get_sender
74
+ @config[:from] || Hulaki::Config['email']['from']
75
+ end
76
+
77
+ def handle_search_and_clipboard
78
+ puts
79
+ response = Hulaki::SearchEngine.new.perform(@config.search_keyword)
80
+ Hulaki::Presenter.new(response).display if response
81
+
82
+ if @config.copy_phone_number
83
+ number = response[0][0]['phone_1___value'].gsub(' ', '')
84
+ Clipboard.copy number
85
+ puts "Number '#{number.underline}' is copied to your clipboard"
86
+ puts
87
+ end
88
+ end
89
+
90
+ end
@@ -2,10 +2,10 @@ class Hulaki::EmailValidator
2
2
  EmailRegex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
3
3
 
4
4
  def initialize(options = {})
5
- @sender_email = options[:from]
5
+ @sender_email = options[:from]
6
6
  @reciepient_email = options[:to]
7
- @message = options[:message]
8
- @errors = {}
7
+ @message = options[:message]
8
+ @errors = {}
9
9
  end
10
10
 
11
11
  def validates_presence
@@ -0,0 +1,73 @@
1
+ require 'mail'
2
+
3
+ class Hulaki::Mailer
4
+ attr_reader :params, :config
5
+
6
+ TEMPLATE_PATH = ENV['template_path'] || '~/hulaki/template.html.erb'
7
+
8
+ def initialize(params={})
9
+ @params = params
10
+ @config = Hulaki::Config["email"]
11
+ end
12
+
13
+ def deliver
14
+ # Fixme: validation has no purpose for the time being
15
+ validate(@reciever, config["from"], params[:message])
16
+ configure_defaults
17
+ prepare_email
18
+ @mail.deliver
19
+ "sent"
20
+ end
21
+
22
+ private
23
+ def prepare_email
24
+ email_body = params[:message]
25
+
26
+ # sender set from command-line will take precedence over that in `config.yml`
27
+ from = params[:from] || config["from"]
28
+ to = params[:to]
29
+ subject = params[:subject]
30
+
31
+ if config['use_template'] == true
32
+ content = ERB.new(File.read(File.expand_path(TEMPLATE_PATH))).result(binding)
33
+ else
34
+ content = email_body
35
+ end
36
+
37
+ @mail = Mail.new do
38
+ to to
39
+ from from
40
+ subject subject
41
+
42
+ html_part do
43
+ content_type 'text/html; charset=UTF-8'
44
+ body content
45
+ end
46
+ end
47
+ end
48
+
49
+ def configure_defaults
50
+ # instance variables are not available inside the block below; so
51
+ # localizing the variable
52
+ env = @config
53
+ delivery_mode = ENV['mode'] == 'test' ? 'test' : :smtp
54
+ Mail.defaults do
55
+ delivery_method delivery_mode,
56
+ {
57
+ :address => env["address"],
58
+ :port => env["port"],
59
+ :domain => env["domain"],
60
+ :user_name => env["user_name"],
61
+ :password => env["password"],
62
+ :authentication => env["authentication"],
63
+ :enable_starttls_auto => true
64
+ }
65
+ end
66
+ end
67
+
68
+ # returns [Hash] Error
69
+ def validate(reciever, sender, message)
70
+ validator = Hulaki::EmailValidator.new(from: reciever, to: sender, message: message)
71
+ validator.validates_format && validator.validates_presence
72
+ end
73
+ end
@@ -1,16 +1,18 @@
1
1
  require_relative 'recursive_ostruct'
2
2
  require 'fileutils'
3
3
  require 'optparse'
4
+ # require File.expand_path('../string_modifier', __FILE__)
4
5
  require_relative 'presenter'
5
6
  class Hulaki::OptionParser
6
7
  def initialize
7
8
  @config = RecursiveOstruct.ostruct(
8
9
  {
9
10
  to: [],
10
- from: [],
11
+ from: nil,
11
12
  subject: 'Mic testing',
12
13
  message: 'sample message',
13
- command: 'help'
14
+ command: 'help',
15
+ gateway: nil
14
16
  })
15
17
  end
16
18
 
@@ -21,96 +23,104 @@ class Hulaki::OptionParser
21
23
 
22
24
  def options
23
25
  OptionParser.new do |opts|
24
- opts.banner = [
25
- 'Usage: ',
26
- '------- Search --------',
27
- '$ hulaki -s search-string',
28
- '# Example: Hulaki features fuzzy search',
29
- '# $ hulaki -s smithjohn',
30
- '# $ hulaki -s johsmith',
31
- '# $ hulaki -s smijohnth',
32
- '',
33
- '------- SMS --------',
34
- '$ hulaki -t +977xxxxxxxxxx -m "Message to be sent"',
35
- '------- EMAIL --------',
36
- ].join("\n")
26
+ opts.banner =
27
+ "Usage: \n"\
28
+ "------- Search --------\n"\
29
+ "$ hulaki -s search-string\n"\
30
+ "# Example: Hulaki features fuzzy search\n"\
31
+ "$ hulaki -s smithjohn\n"\
32
+ "$ hulaki -s johsmith\n"\
33
+ "$ hulaki -s smijohnth\n"\
34
+ "\n"\
35
+ "------- Copy PhoneNumber to ClipBoard -----------\n"\
36
+ "$ hulaki -s smithjohn -c \n"\
37
+ "# You will see phone_number of the top result copied to ClipBoard \n"\
38
+ "# Number '+97798xxx66455' is copied to your clipboard\n"\
39
+ "\n"\
40
+ "------- SMS --------\n"\
41
+ "$ hulaki -t +977xxxxxxxxxx -m \"Message to be sent\"\n"\
42
+ "\n"\
43
+ "# You can even select a specific SMS Gateway\n"\
44
+ "$ hulaki -t +977xxxxxxxxxx -m \"Message to be sent\" -g nexmo\n"\
45
+ "$ hulaki -t +61xxxxxxxxxx -m \"Message to be sent\" -g twilio\n"\
46
+ "$ hulaki -t +1xxxxxxxxxx -m \"Message to be sent\" -g sparrow\n"\
47
+ "\n"\
48
+ "# You can even broadcast SMSes selecting a specific SMS Gateway\n"\
49
+ "# However, keep in mind that multiple SMS request will go to the server\n"\
50
+ "$ hulaki -t +977xxxxxxxxxx,+9779832xxxxxx -m \"Message to be sent\" -g nexmo\n"\
51
+ "\n"\
52
+ "# You can also change the name that appears on recipient's Phone using `-f` switch. This only works with Nexmo\n"\
53
+ "$ hulaki -t +977xxxxxxxxxx,+9779832xxxxxx -m \"Message to be sent\" -g nexmo -f \"Hero Dai!\"\n"\
54
+ "\n"\
55
+ "------- EMAIL --------\n"\
56
+ "$ hulaki -t someone@example.com -S \"Subject of the email\" -m \"Message to be sent\"\n"\
57
+ "$ hulaki -t someone@example.com --subject \"Subject of the email\" -m \"Message to be sent\"\n"\
58
+ "\n"\
59
+ "# You can even broadcast emails, i.e. mutiple recipients\n"\
60
+ "# However, keep in mind that multiple SMTP request will go to the server. No `CC`, `BCC` will be used\n"\
61
+ "$ hulaki -t someone@example.com,nextperson@email.com --subject \"Subject of the email\" -m \"Message to be sent\"\n"\
62
+ "\n"\
63
+ "# You can also change your sender id using `-f` switch\n"\
64
+ "$ hulaki -t someone@example.com -S \"Subject of the email\" -m \"Message to be sent\" -f \"My Name<anonymous@example.com>\" \n"\
65
+ "\n"\
66
+ "------- EMAIL TEMPLATES --------\n"\
67
+ "# You are allowed to have an Email template in HTML format at `~/hulaki/template.html.erb` which\n"\
68
+ "# will be copied when you use `-i` switch. If you have `use_template` setting set to `true` then only\n"\
69
+ "# you will be able to use the template\n"\
70
+ "$ hulaki -t someone@example.com -S \"Subject of the email\" -m \"Messagopts.to_se to be sent\"\n"
71
+
72
+
37
73
 
38
74
  opts.separator ''
39
75
  opts.separator 'Specific options:'
40
76
 
41
- opts.on('-t x,y,z', '--to x,y,z', Array, 'list of recipient, can be') do |list|
42
- @config.to = list
77
+ # This can be list of emails or phonenumbers separated by commas `,`
78
+ opts.on('-t x,y,z', '--to x,y,z', Array, 'list of recipient, can be') do |recipient_list|
79
+ @config.to = recipient_list
43
80
  end
44
81
 
45
- opts.on('-m [Message]', '--message [Message]', String, 'Message to be sent to recipient') do |msg|
46
- @config.message = msg
82
+ opts.on('-m [Message]', '--message [Message]', String, 'Message to be sent to recipient') do |message|
83
+ @config.message = message
47
84
  end
85
+ "\n"\
48
86
 
49
- opts.on('-S [Subject]', '--subject [Subject]', String, 'Subject to email') do |sub|
50
- @config.subject = sub
87
+ opts.on('-S [Subject]', '--subject [Subject]', String, 'Subject to email') do |subject|
88
+ @config.subject = subject
51
89
  end
52
90
 
53
- opts.on('-f x,y,z', '--from x,y,z', Array, 'Help / Examples') do |sender_list|
54
- @config.to = sender_list
91
+ opts.on('-c', '--copy', nil, 'Copy phone-number at top to ClipBoard; Linux users need to install `xclip`.') do
92
+ @config.copy_phone_number = true
55
93
  end
56
94
 
57
- opts.on('-s [name/contact]', '--search [name/contact]', String, 'Search keyword') do |word|
58
- response = Hulaki::SearchEngine.new.perform(word)
59
- Hulaki::Presenter.new(response).display if response
60
- exit
95
+ opts.on('-g [Gateway Name]', '--gateway [Gateway Name]', String, 'Name of the gateway, `nexmo`, `twilio`, `sparrow` are currently supported') do |gateway|
96
+ @config.gateway = gateway
97
+ end
98
+
99
+ opts.on('-f [Sender]', '--from [Sender]', String, "name <email> | PhoneNumber; this will take precedence over `from` in `config.yml`.") do |sender|
100
+ @config.from = sender
101
+ end
102
+
103
+ opts.on('-s [name/contact]', '--search [name/contact]', String, 'Search keyword') do |search_keyword|
104
+ @config.search_keyword = search_keyword
61
105
  end
62
106
 
63
107
  # ----------------------------------------------------------------------
64
108
  opts.on('-h', '--help', 'Help / Examples') do
65
- Hulaki::Logger.log opts.banner
109
+ puts Utils.present(opts)
66
110
  exit
67
111
  end
68
112
 
69
113
  opts.on('-l', '--list', 'list all the options available') do
70
- puts opts
114
+ puts Utils.present(opts)
71
115
  exit
72
116
  end
73
117
 
74
- opts.on('-i', '--install', 'Creates ~/hulaki/config.yml') do
75
- create_dir
76
- start_copying_file
118
+ opts.on('-i', '--install', 'Creates ~/hulaki/config.yml, `template.html.erb`. Will ask you if have to replace them') do
119
+ Utils.install_dependencies
120
+ Utils.create_dir
121
+ Utils.start_copying_file
77
122
  exit
78
123
  end
79
124
  end
80
125
  end
81
-
82
- def create_dir
83
- FileUtils.mkdir(File.expand_path('~/hulaki'))
84
- rescue Errno::EEXIST
85
- puts 'Directory already exists.'
86
- end
87
-
88
- def start_copying_file
89
- this_file = __FILE__
90
- file_path = File.expand_path('../../lib/hulaki/config/config_sample.yml',
91
- File.dirname(this_file))
92
- desc_file = File.expand_path('~/hulaki/config.yml')
93
- if File.exist?(desc_file)
94
- puts "Looks like the file '#{desc_file}' already exists."
95
- puts 'shall we forcefully override the file?(yes/no)'
96
- handle_conflict(file_path, desc_file)
97
- else
98
- copy_file(file_path, desc_file)
99
- end
100
- end
101
-
102
- def handle_conflict(file_path, desc_file)
103
- input = gets().chomp()
104
- if %w{yes y}.include?(input.downcase)
105
- copy_file(file_path, desc_file)
106
- else
107
- puts 'You choose to leave it as it is.'
108
- end
109
- end
110
-
111
- def copy_file(file_path, desc_file)
112
- puts "Creating file '~/hulaki/config.yml' ..."
113
- FileUtils.cp(file_path, desc_file)
114
- puts "Created file '~/hulaki/config.yml' ..."
115
- end
116
126
  end
@@ -1,17 +1,23 @@
1
+ require 'terminal-table'
1
2
  class Hulaki::Presenter
2
3
  def initialize(data)
3
- @data = [data].flatten
4
+ no_of_results = Hulaki::Config['search'] && Hulaki::Config['search']['no_of_results'] || 10
5
+ @data = data[0..(no_of_results - 1)]
4
6
  end
5
7
 
6
8
  def display
7
- puts '~' * 100
8
- puts 'Search Results'
9
- puts '~' * 100
10
- @data[0..5].each do |row|
11
- email = row.fetch('e_mail_1___value', 'N/A') rescue 'N/A'
12
- phone_number = row.fetch('phone_1___value', 'N/A') rescue 'N/A'
13
- puts "name: #{row['name']}, phone: #{phone_number}, email: #{email}" rescue nil
9
+ data = @data.map(&:first).map.with_index do |row, index|
10
+ [index+1, row['name'].bold, row['phone_1___value'].to_s.green, row['phone_2___value'], row['email']]
14
11
  end
15
- puts '~' * 100
12
+
13
+ table = Terminal::Table.new :title => "Hulaki #{Hulaki::VERSION}",
14
+ :headings => ['S.N', 'Name', 'Phone 1', 'Phone 2', 'Email'],
15
+ :rows => data
16
+
17
+ table.align_column(2, :right)
18
+ table.align_column(3, :right)
19
+ table.align_column(4, :right)
20
+
21
+ puts table
16
22
  end
17
23
  end
@@ -1,4 +1,16 @@
1
1
  require 'ostruct'
2
+ # = RecursiveOstruct, a tool to convert every hash inside a Hash/Array to OpenStruct object
3
+ # One of the benifit is, it will respond to very sort of messages like
4
+ # Example:
5
+ #
6
+ # newHash = {'a' => 12}
7
+ # wrappedHash = RecursiveOstruct.new(newHash)
8
+ # wrappedHash.a # => 12 : a valid call
9
+ # wrappedHash['a'] # => 12 : a valid call
10
+ # wrappedHash[:a] # => 12 : a valid call
11
+ #
12
+ # It is a option for those who do not want to use Rails::ActiveSupport's `HashWithIndifferentAccess`
13
+ #
2
14
  class RecursiveOstruct
3
15
  def self.ostruct(object)
4
16
  if object.is_a?(Hash)