instabot 0.1.81 → 0.1.90
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/instabot.rb +11 -11
- data/lib/instabot/actions.rb +56 -56
- data/lib/instabot/banner.rb +16 -16
- data/lib/instabot/config.rb +51 -51
- data/lib/instabot/core.rb +113 -108
- data/lib/instabot/create_protocol.rb +51 -51
- data/lib/instabot/grabber.rb +114 -112
- data/lib/instabot/logger.rb +61 -61
- data/lib/instabot/login.rb +62 -62
- data/lib/instabot/modes.rb +228 -239
- data/lib/instabot/tor_protocol.rb +81 -81
- data/lib/instabot/version.rb +5 -5
- metadata +3 -3
@@ -1,51 +1,51 @@
|
|
1
|
-
module Protocol
|
2
|
-
def create_protocol
|
3
|
-
if options[:use_tor]
|
4
|
-
@tor = TorProtocol::Tor.new
|
5
|
-
end
|
6
|
-
@agent = Mechanize.new
|
7
|
-
@agent.max_history = 2
|
8
|
-
# @agent.ca_file = './cacert.pem' # Because i use windows and... you know... ._.
|
9
|
-
@agent.user_agent_alias = ['Mac Mozilla', 'Mac Safari', 'Windows IE 6', 'Windows IE 7', 'Windows IE 8', 'Windows IE 9', 'Windows Mozilla', 'Windows Chrome'].sample
|
10
|
-
if options[:use_proxy]
|
11
|
-
proxy = options[:proxy]
|
12
|
-
if proxy.size == 2
|
13
|
-
@agent.set_proxy(proxy[0], proxy[1].to_i)
|
14
|
-
else
|
15
|
-
@agent.set_proxy(proxy[0], proxy[1].to_i, proxy[2], proxy[3])
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
if options[:use_tor]
|
20
|
-
@agent.agent.set_socks('localhost', 9050)
|
21
|
-
log('tor has been started', 'CREATE_PROTOCOL')
|
22
|
-
end
|
23
|
-
|
24
|
-
puts 'PROCESSING: '.cyan.bold + 'protocol created'
|
25
|
-
log('Protocol successfully created', 'CREATE_PROTOCOL')
|
26
|
-
end
|
27
|
-
|
28
|
-
def get_page(url)
|
29
|
-
@agent.get(url)
|
30
|
-
end
|
31
|
-
|
32
|
-
def set_mechanic_data(params = {})
|
33
|
-
@cookies = Hash[@agent.cookies.map { |key, _value| [key.name, key.value] }]
|
34
|
-
@params = params
|
35
|
-
@headers = {
|
36
|
-
'Cookie' => "mid=#{@cookies['mid']}; csrftoken=#{@cookies['csrftoken']}; sessionid=#{@cookies['sessionid']}; ds_user_id=#{@cookies['ds_user_id']}; rur=#{@cookies['rur']}; s_network=#{@cookies['s_network']}; ig_pr= 1; ig_vw=1920",
|
37
|
-
'X-CSRFToken' => (@cookies['csrftoken']).to_s,
|
38
|
-
'X-Requested-With' => 'XMLHttpRequest',
|
39
|
-
'Content-Type' => 'application/x-www-form-urlencoded',
|
40
|
-
'X-Instagram-AJAX' => '1',
|
41
|
-
'Accept' => 'application/json, text/javascript, */*',
|
42
|
-
'User-Agent' => 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0',
|
43
|
-
'Accept-Encoding' => 'gzip, deflate',
|
44
|
-
'Accept-Language' => 'ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4',
|
45
|
-
'Connection' => 'keep-alive',
|
46
|
-
'Host' => 'www.instagram.com',
|
47
|
-
'Origin' => 'https://www.instagram.com',
|
48
|
-
'Referer' => 'https://www.instagram.com/'
|
49
|
-
}
|
50
|
-
end
|
51
|
-
end
|
1
|
+
module Protocol
|
2
|
+
def create_protocol
|
3
|
+
if options[:use_tor]
|
4
|
+
@tor = TorProtocol::Tor.new
|
5
|
+
end
|
6
|
+
@agent = Mechanize.new
|
7
|
+
@agent.max_history = 2
|
8
|
+
# @agent.ca_file = './cacert.pem' # Because i use windows and... you know... ._.
|
9
|
+
@agent.user_agent_alias = ['Mac Mozilla', 'Mac Safari', 'Windows IE 6', 'Windows IE 7', 'Windows IE 8', 'Windows IE 9', 'Windows Mozilla', 'Windows Chrome'].sample
|
10
|
+
if options[:use_proxy]
|
11
|
+
proxy = options[:proxy]
|
12
|
+
if proxy.size == 2
|
13
|
+
@agent.set_proxy(proxy[0], proxy[1].to_i)
|
14
|
+
else
|
15
|
+
@agent.set_proxy(proxy[0], proxy[1].to_i, proxy[2], proxy[3])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if options[:use_tor]
|
20
|
+
@agent.agent.set_socks('localhost', 9050)
|
21
|
+
log('tor has been started', 'CREATE_PROTOCOL')
|
22
|
+
end
|
23
|
+
|
24
|
+
puts 'PROCESSING: '.cyan.bold + 'protocol created'
|
25
|
+
log('Protocol successfully created', 'CREATE_PROTOCOL')
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_page(url)
|
29
|
+
@agent.get(url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_mechanic_data(params = {})
|
33
|
+
@cookies = Hash[@agent.cookies.map { |key, _value| [key.name, key.value] }]
|
34
|
+
@params = params
|
35
|
+
@headers = {
|
36
|
+
'Cookie' => "mid=#{@cookies['mid']}; csrftoken=#{@cookies['csrftoken']}; sessionid=#{@cookies['sessionid']}; ds_user_id=#{@cookies['ds_user_id']}; rur=#{@cookies['rur']}; s_network=#{@cookies['s_network']}; ig_pr= 1; ig_vw=1920",
|
37
|
+
'X-CSRFToken' => (@cookies['csrftoken']).to_s,
|
38
|
+
'X-Requested-With' => 'XMLHttpRequest',
|
39
|
+
'Content-Type' => 'application/x-www-form-urlencoded',
|
40
|
+
'X-Instagram-AJAX' => '1',
|
41
|
+
'Accept' => 'application/json, text/javascript, */*',
|
42
|
+
'User-Agent' => 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0',
|
43
|
+
'Accept-Encoding' => 'gzip, deflate',
|
44
|
+
'Accept-Language' => 'ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4',
|
45
|
+
'Connection' => 'keep-alive',
|
46
|
+
'Host' => 'www.instagram.com',
|
47
|
+
'Origin' => 'https://www.instagram.com',
|
48
|
+
'Referer' => 'https://www.instagram.com/'
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
data/lib/instabot/grabber.rb
CHANGED
@@ -1,112 +1,114 @@
|
|
1
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
-
|
3
|
-
module Grabber
|
4
|
-
def handle_user_information_data_by_user_id(user_id)
|
5
|
-
puts '[+] '.cyan + "Trying to get user (#{user_id}) information"
|
6
|
-
log("Trying to get user (#{user_id}) information", 'GRABBER')
|
7
|
-
user_page = "https://www.instagram.com/web/friendships/#{user_id}/follow/"
|
8
|
-
response = get_page(user_page)
|
9
|
-
last_page = response.uri.to_s
|
10
|
-
username = last_page.split('/')[3]
|
11
|
-
response = @agent.get("https://instagram.com/#{username}/?__a=1")
|
12
|
-
data = parse_response(response.body)
|
13
|
-
|
14
|
-
set_user_information(data)
|
15
|
-
end
|
16
|
-
|
17
|
-
def handle_user_information_data_by_user_name(username)
|
18
|
-
puts '[+] '.cyan + "Trying to get user (#{username}) information"
|
19
|
-
log("Trying to get user (#{username}) information", 'GRABBER')
|
20
|
-
response = @agent.get("https://instagram.com/#{username}/?__a=1")
|
21
|
-
data = parse_response(response.body)
|
22
|
-
|
23
|
-
set_user_information(data)
|
24
|
-
end
|
25
|
-
|
26
|
-
def handle_media_information_data(media_id)
|
27
|
-
puts '[+] '.cyan + "Trying to get media (#{media_id}) information"
|
28
|
-
log("Trying to get media (#{media_id}) information", 'GRABBER')
|
29
|
-
response = @agent.get("https://www.instagram.com/p/#{media_id}/?__a=1")
|
30
|
-
data = parse_response(response.body)
|
31
|
-
|
32
|
-
set_media_information(data)
|
33
|
-
|
34
|
-
unless @infinite_tags == true
|
35
|
-
tags = @media_information[:text].encode('UTF-8', invalid: :replace, undef: :replace, replace: '?').split(/\W+/)
|
36
|
-
id = 0
|
37
|
-
tags.each do |tag|
|
38
|
-
if id < options[:add_tag_per_post]
|
39
|
-
if tag == '_' || tag == '' || tag.nil? || tag.include?('?')
|
40
|
-
tags.delete(tag)
|
41
|
-
else
|
42
|
-
id += 1
|
43
|
-
Config.options.tags << tag
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
puts '[+] '.cyan + '[' + id.to_s.yellow + '] New tags added'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def set_user_information(data)
|
52
|
-
@user_information = {
|
53
|
-
followers: data.deep_find('followed_by')['count'],
|
54
|
-
following: data.deep_find('follows')['count'],
|
55
|
-
follows_viewer: data.deep_find('follows_viewer'),
|
56
|
-
followed_by_viewer: data.deep_find('followed_by_viewer'),
|
57
|
-
requested_by_viewer: data.deep_find('requested_by_viewer'),
|
58
|
-
is_private: data.deep_find('is_private'),
|
59
|
-
is_verified: data.deep_find('is_verified'),
|
60
|
-
username: data.deep_find('username'),
|
61
|
-
media_count: data.deep_find('media')['count'],
|
62
|
-
medias: data.deep_find('media')['nodes'],
|
63
|
-
full_name: data.deep_find('full_name'),
|
64
|
-
id: data.deep_find('id')
|
65
|
-
}.with_indifferent_access
|
66
|
-
end
|
67
|
-
|
68
|
-
def set_media_information(data)
|
69
|
-
@media_information = {
|
70
|
-
id: data.deep_find('id'),
|
71
|
-
full_name: data.deep_find('full_name'),
|
72
|
-
owner: data.deep_find('owner')['username'],
|
73
|
-
is_video: data.deep_find('is_video'),
|
74
|
-
comments_disabled: data.deep_find('comments_disabled'),
|
75
|
-
viewer_has_liked: data.deep_find('viewer_has_liked'),
|
76
|
-
has_blocked_viewer: data.deep_find('has_blocked_viewer'),
|
77
|
-
followed_by_viewer: data.deep_find('followed_by_viewer'),
|
78
|
-
is_private: data.deep_find('is_private'),
|
79
|
-
is_verified: data.deep_find('is_verified'),
|
80
|
-
requested_by_viewer: data.deep_find('requested_by_viewer'),
|
81
|
-
text: data.deep_find('text')
|
82
|
-
}.with_indifferent_access
|
83
|
-
end
|
84
|
-
|
85
|
-
def search(tags = [])
|
86
|
-
used_tags = []
|
87
|
-
tags.each do |tag|
|
88
|
-
puts '[+] '.cyan + "Searching in hashtag [##{tag}]"
|
89
|
-
log("Searching in hashtags [##{tag}]", 'GRABBER')
|
90
|
-
url = "https://www.instagram.com/explore/tags/#{tag}/?__a=1"
|
91
|
-
response = @agent.get(url)
|
92
|
-
data = parse_response(response.body)
|
93
|
-
owners = data.deep_find_all('owner')
|
94
|
-
media_codes = data.deep_find_all('shortcode')
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
data
|
111
|
-
|
112
|
-
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
|
3
|
+
module Grabber
|
4
|
+
def handle_user_information_data_by_user_id(user_id)
|
5
|
+
puts '[+] '.cyan + "Trying to get user (#{user_id}) information"
|
6
|
+
log("Trying to get user (#{user_id}) information", 'GRABBER')
|
7
|
+
user_page = "https://www.instagram.com/web/friendships/#{user_id}/follow/"
|
8
|
+
response = get_page(user_page)
|
9
|
+
last_page = response.uri.to_s
|
10
|
+
username = last_page.split('/')[3]
|
11
|
+
response = @agent.get("https://instagram.com/#{username}/?__a=1")
|
12
|
+
data = parse_response(response.body)
|
13
|
+
|
14
|
+
set_user_information(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_user_information_data_by_user_name(username)
|
18
|
+
puts '[+] '.cyan + "Trying to get user (#{username}) information"
|
19
|
+
log("Trying to get user (#{username}) information", 'GRABBER')
|
20
|
+
response = @agent.get("https://instagram.com/#{username}/?__a=1")
|
21
|
+
data = parse_response(response.body)
|
22
|
+
|
23
|
+
set_user_information(data)
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_media_information_data(media_id)
|
27
|
+
puts '[+] '.cyan + "Trying to get media (#{media_id}) information"
|
28
|
+
log("Trying to get media (#{media_id}) information", 'GRABBER')
|
29
|
+
response = @agent.get("https://www.instagram.com/p/#{media_id}/?__a=1")
|
30
|
+
data = parse_response(response.body)
|
31
|
+
|
32
|
+
set_media_information(data)
|
33
|
+
|
34
|
+
unless @infinite_tags == true
|
35
|
+
tags = @media_information[:text].encode('UTF-8', invalid: :replace, undef: :replace, replace: '?').split(/\W+/)
|
36
|
+
id = 0
|
37
|
+
tags.each do |tag|
|
38
|
+
if id < options[:add_tag_per_post]
|
39
|
+
if tag == '_' || tag == '' || tag.nil? || tag.include?('?')
|
40
|
+
tags.delete(tag)
|
41
|
+
else
|
42
|
+
id += 1
|
43
|
+
Config.options.tags << tag
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts '[+] '.cyan + '[' + id.to_s.yellow + '] New tags added'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_user_information(data)
|
52
|
+
@user_information = {
|
53
|
+
followers: data.deep_find('followed_by')['count'],
|
54
|
+
following: data.deep_find('follows')['count'],
|
55
|
+
follows_viewer: data.deep_find('follows_viewer'),
|
56
|
+
followed_by_viewer: data.deep_find('followed_by_viewer'),
|
57
|
+
requested_by_viewer: data.deep_find('requested_by_viewer'),
|
58
|
+
is_private: data.deep_find('is_private'),
|
59
|
+
is_verified: data.deep_find('is_verified'),
|
60
|
+
username: data.deep_find('username'),
|
61
|
+
media_count: data.deep_find('media')['count'],
|
62
|
+
medias: data.deep_find('media')['nodes'],
|
63
|
+
full_name: data.deep_find('full_name'),
|
64
|
+
id: data.deep_find('id')
|
65
|
+
}.with_indifferent_access
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_media_information(data)
|
69
|
+
@media_information = {
|
70
|
+
id: data.deep_find('id'),
|
71
|
+
full_name: data.deep_find('full_name'),
|
72
|
+
owner: data.deep_find('owner')['username'],
|
73
|
+
is_video: data.deep_find('is_video'),
|
74
|
+
comments_disabled: data.deep_find('comments_disabled'),
|
75
|
+
viewer_has_liked: data.deep_find('viewer_has_liked'),
|
76
|
+
has_blocked_viewer: data.deep_find('has_blocked_viewer'),
|
77
|
+
followed_by_viewer: data.deep_find('followed_by_viewer'),
|
78
|
+
is_private: data.deep_find('is_private'),
|
79
|
+
is_verified: data.deep_find('is_verified'),
|
80
|
+
requested_by_viewer: data.deep_find('requested_by_viewer'),
|
81
|
+
text: data.deep_find('text')
|
82
|
+
}.with_indifferent_access
|
83
|
+
end
|
84
|
+
|
85
|
+
def search(tags = [])
|
86
|
+
used_tags = []
|
87
|
+
tags.each do |tag|
|
88
|
+
puts '[+] '.cyan + "Searching in hashtag [##{tag}]"
|
89
|
+
log("Searching in hashtags [##{tag}]", 'GRABBER')
|
90
|
+
url = "https://www.instagram.com/explore/tags/#{tag}/?__a=1"
|
91
|
+
response = @agent.get(url)
|
92
|
+
data = parse_response(response.body)
|
93
|
+
owners = data.deep_find_all('owner')
|
94
|
+
media_codes = data.deep_find_all('shortcode')
|
95
|
+
log("didn't find anything by hashtag ##{tag}", 'GRABBER')
|
96
|
+
next if owners.nil? || media_codes.nil?
|
97
|
+
owners.map { |id| users << id['id'] if !id['id'].nil? }
|
98
|
+
media_codes.map { |code| medias << code if !code.nil? }
|
99
|
+
used_tags << tag
|
100
|
+
end
|
101
|
+
Config.options.tags = Config.options.tags - used_tags
|
102
|
+
puts '[+] '.cyan + 'Total grabbed users [' + users.size.to_s.yellow + ']'
|
103
|
+
puts '[+] '.cyan + 'Total grabbed medias [' + medias.size.to_s.yellow + ']'
|
104
|
+
log("Total grabbed users(#{users.size}) & Total grabbed medias(#{medias.size})", 'GRABBER')
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def parse_response(body)
|
110
|
+
data = JSON.parse(body)
|
111
|
+
data.extend Hashie::Extensions::DeepFind
|
112
|
+
data
|
113
|
+
end
|
114
|
+
end
|
data/lib/instabot/logger.rb
CHANGED
@@ -1,61 +1,61 @@
|
|
1
|
-
module Log
|
2
|
-
def check_log_files
|
3
|
-
puts 'PROCESSING: '.cyan.bold + 'checking log files'
|
4
|
-
log('checking log files', 'LOGGER')
|
5
|
-
unless File.exists?(@logs_dir)
|
6
|
-
Dir.mkdir(@logs_dir)
|
7
|
-
end
|
8
|
-
|
9
|
-
if options[:pre_load]
|
10
|
-
if Dir.exists?('./logs')
|
11
|
-
files = %w[commented_medias followed_users liked_medias unliked_medias unfollowed_users]
|
12
|
-
files.each do |file|
|
13
|
-
if File.exists?("./logs/#{file}.txt")
|
14
|
-
File.open("./logs/#{file}.txt", 'r+') do |buffer|
|
15
|
-
data = buffer.read.split(',')
|
16
|
-
@local_stroage[file.to_sym] = data
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
def write_file(filename, text = '')
|
26
|
-
File.open("#{@logs_dir}/#{filename}", 'a+') { |f| f.print "#{text.chomp}," }
|
27
|
-
end
|
28
|
-
|
29
|
-
def log(text = '', from = '', logs_status=options[:log_status])
|
30
|
-
if logs_status
|
31
|
-
time = Time.new.strftime('%H:%M:%S %y-%m-%d')
|
32
|
-
if File.exists?(@logs_dir)
|
33
|
-
File.open("#{@logs_dir}/logs-#{@started_time}.log", 'a+') do |log_file|
|
34
|
-
log_file.puts "[#{@log_counter}] [#{time}] [#{from}] -- : #{text}"
|
35
|
-
end
|
36
|
-
else
|
37
|
-
Dir.mkdir(@logs_dir)
|
38
|
-
end
|
39
|
-
@log_counter += 1
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def save_action_log(id = 0, type = :default)
|
44
|
-
case type
|
45
|
-
when :follow
|
46
|
-
write_file('followed_users.txt', id)
|
47
|
-
when :unfollow
|
48
|
-
write_file('unfollowed_users.txt', id)
|
49
|
-
when :like
|
50
|
-
write_file('liked_medias.txt', id)
|
51
|
-
when :unlike
|
52
|
-
write_file('unliked_medias.txt', id)
|
53
|
-
when :comment
|
54
|
-
write_file('commented_medias.txt', id)
|
55
|
-
when :default
|
56
|
-
puts 'please choose a type'
|
57
|
-
else
|
58
|
-
puts 'please choose a type'
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
1
|
+
module Log
|
2
|
+
def check_log_files
|
3
|
+
puts 'PROCESSING: '.cyan.bold + 'checking log files'
|
4
|
+
log('checking log files', 'LOGGER')
|
5
|
+
unless File.exists?(@logs_dir)
|
6
|
+
Dir.mkdir(@logs_dir)
|
7
|
+
end
|
8
|
+
|
9
|
+
if options[:pre_load]
|
10
|
+
if Dir.exists?('./logs')
|
11
|
+
files = %w[commented_medias followed_users liked_medias unliked_medias unfollowed_users]
|
12
|
+
files.each do |file|
|
13
|
+
if File.exists?("./logs/#{file}.txt")
|
14
|
+
File.open("./logs/#{file}.txt", 'r+') do |buffer|
|
15
|
+
data = buffer.read.split(',')
|
16
|
+
@local_stroage[file.to_sym] = data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def write_file(filename, text = '')
|
26
|
+
File.open("#{@logs_dir}/#{filename}", 'a+') { |f| f.print "#{text.chomp}," }
|
27
|
+
end
|
28
|
+
|
29
|
+
def log(text = '', from = '', logs_status=options[:log_status])
|
30
|
+
if logs_status
|
31
|
+
time = Time.new.strftime('%H:%M:%S %y-%m-%d')
|
32
|
+
if File.exists?(@logs_dir)
|
33
|
+
File.open("#{@logs_dir}/logs-#{@started_time}.log", 'a+') do |log_file|
|
34
|
+
log_file.puts "[#{@log_counter}] [#{time}] [#{from}] -- : #{text}"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
Dir.mkdir(@logs_dir)
|
38
|
+
end
|
39
|
+
@log_counter += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def save_action_log(id = 0, type = :default)
|
44
|
+
case type
|
45
|
+
when :follow
|
46
|
+
write_file('followed_users.txt', id)
|
47
|
+
when :unfollow
|
48
|
+
write_file('unfollowed_users.txt', id)
|
49
|
+
when :like
|
50
|
+
write_file('liked_medias.txt', id)
|
51
|
+
when :unlike
|
52
|
+
write_file('unliked_medias.txt', id)
|
53
|
+
when :comment
|
54
|
+
write_file('commented_medias.txt', id)
|
55
|
+
when :default
|
56
|
+
puts 'please choose a type'
|
57
|
+
else
|
58
|
+
puts 'please choose a type'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|