brandkit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +164 -0
- data/bin/brandkit +6 -0
- data/lib/api_clients/namecheap_client.rb +75 -0
- data/lib/brand_kit.rb +195 -0
- data/lib/domain_checker.rb +144 -0
- data/lib/social_username_checker.rb +81 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fec24aa78cc7f4ac5dd130e84d05e7fa2d119baef30d403f644ce27d3f36e326
|
4
|
+
data.tar.gz: 444d5f42e748f3c53ffeaa23bde9bc79e2215ea851875869ad81508e45d4cc4a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84331e4f351c06029a594164d06cf475275436ed6a09f645c2f35e078d5370250e94503c9d6eaee0af25a9df8c35254c265ef958fb9748d7039d1a049a421cd1
|
7
|
+
data.tar.gz: 7de99fac92531a86570b77f2a5ce6bc60a729fa82c3be069bdfe07a67bb6e48b2e426a44b84daf53526600d8ce85cfcca14d896230d316043570b8afbf6b68df
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Rajan Bhattarai
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# BrandKit
|
2
|
+
|
3
|
+
**BrandKit** is a modern, interactive CLI tool designed to help entrepreneurs,
|
4
|
+
developers, and creatives quickly check the availability of domains and social
|
5
|
+
media usernames — all from the command line. It combines domain availability
|
6
|
+
checks via the Namecheap API with username availability checks on popular social
|
7
|
+
platforms, helping you secure your brand identity in one place.
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
- **Domain Availability Check**
|
14
|
+
Checks if a domain (e.g. `example.com`) is available to register using the
|
15
|
+
Namecheap API. If a domain is not provided with a TLD, it suggests popular
|
16
|
+
extensions like `.com`, `.net`, `.io`, etc., and shows their availability in a
|
17
|
+
table.
|
18
|
+
|
19
|
+
- **Social Username Availability Check**
|
20
|
+
Interactively checks if your chosen username is available on multiple social
|
21
|
+
media platforms including GitHub, Twitter, Instagram, TikTok, LinkedIn, and
|
22
|
+
more.
|
23
|
+
|
24
|
+
- **Interactive CLI Experience**
|
25
|
+
Powered by TTY::Prompt for user-friendly menus, multi-selects, and prompts,
|
26
|
+
plus colorful terminal output for better readability.
|
27
|
+
|
28
|
+
- **Clean & Extensible Codebase**
|
29
|
+
Modular design with dedicated classes for API communication (
|
30
|
+
`NamecheapClient`), domain checks (`DomainChecker`), and social username
|
31
|
+
checks (`SocialUsernameChecker`).
|
32
|
+
|
33
|
+
- **Dockerized for Easy Setup**
|
34
|
+
Runs inside a container for hassle-free installation and dependency
|
35
|
+
management.
|
36
|
+
|
37
|
+
---
|
38
|
+
|
39
|
+
## Getting Started
|
40
|
+
|
41
|
+
### Prerequisites
|
42
|
+
|
43
|
+
- Ruby 3.x installed locally or use Docker
|
44
|
+
- Namecheap API credentials (API user, API key, username, client IP)
|
45
|
+
- Internet connection to query domain and social media availability
|
46
|
+
|
47
|
+
### Environment Variables
|
48
|
+
|
49
|
+
Before running, set these environment variables in your shell or `.env` file:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
export NAMECHEAP_API_USER=your_api_user
|
53
|
+
export NAMECHEAP_API_KEY=your_api_key
|
54
|
+
export NAMECHEAP_USERNAME=your_namecheap_username
|
55
|
+
export CLIENT_IP=your_client_ip
|
56
|
+
```
|
57
|
+
|
58
|
+
### Installation
|
59
|
+
|
60
|
+
Clone the repo and install dependencies:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
git clone https://github.com/yourusername/brandkit.git
|
64
|
+
cd brandkit
|
65
|
+
bundle install
|
66
|
+
```
|
67
|
+
|
68
|
+
Alternatively, use Docker:
|
69
|
+
|
70
|
+
```bash
|
71
|
+
docker-compose build
|
72
|
+
```
|
73
|
+
|
74
|
+
---
|
75
|
+
|
76
|
+
## Usage
|
77
|
+
|
78
|
+
### Run interactively
|
79
|
+
|
80
|
+
Launch the CLI with an interactive prompt experience, guiding you step-by-step:
|
81
|
+
|
82
|
+
```bash
|
83
|
+
docker-compose run cli
|
84
|
+
```
|
85
|
+
|
86
|
+
Or locally:
|
87
|
+
|
88
|
+
```bash
|
89
|
+
ruby bin/brandkit
|
90
|
+
```
|
91
|
+
|
92
|
+
### Run with command-line arguments
|
93
|
+
|
94
|
+
Provide a domain upfront to skip prompts:
|
95
|
+
|
96
|
+
```bash
|
97
|
+
docker-compose run cli ruby bin/brandkit --domain=example.com
|
98
|
+
```
|
99
|
+
|
100
|
+
This will check the domain immediately and display availability, with an option
|
101
|
+
to check social usernames.
|
102
|
+
|
103
|
+
---
|
104
|
+
|
105
|
+
## Code Overview
|
106
|
+
|
107
|
+
### `BrandKit`
|
108
|
+
|
109
|
+
The main CLI application class coordinating user input, output formatting, and
|
110
|
+
flow control. Uses:
|
111
|
+
|
112
|
+
- `TTY::Prompt` for interactive prompts
|
113
|
+
- `Artii` for ASCII banners
|
114
|
+
- `Colorize` for colored terminal output
|
115
|
+
|
116
|
+
### `DomainChecker`
|
117
|
+
|
118
|
+
Handles domain availability checking logic:
|
119
|
+
|
120
|
+
- Uses the `NamecheapClient` API wrapper to query domain status
|
121
|
+
- Suggests popular TLDs if no extension is provided
|
122
|
+
- Displays results in a neat table format
|
123
|
+
|
124
|
+
### `ApiClients::NamecheapClient`
|
125
|
+
|
126
|
+
API client encapsulating communication with the Namecheap XML API:
|
127
|
+
|
128
|
+
- Authenticates via environment variables
|
129
|
+
- Handles building query parameters and HTTP requests
|
130
|
+
- Parses XML responses and handles errors gracefully
|
131
|
+
|
132
|
+
### `SocialUsernameChecker`
|
133
|
+
|
134
|
+
Checks username availability on popular social platforms by performing HTTP GET
|
135
|
+
requests to profile URLs and interpreting the HTTP response status.
|
136
|
+
|
137
|
+
- Supports GitHub, Twitter, Instagram, Facebook, YouTube, TikTok, Pinterest,
|
138
|
+
LinkedIn, Reddit, Threads
|
139
|
+
- Allows querying single or multiple platforms interactively
|
140
|
+
|
141
|
+
---
|
142
|
+
|
143
|
+
## Contributing
|
144
|
+
|
145
|
+
Contributions are welcome! Please fork the repo and submit pull requests with
|
146
|
+
descriptive commit messages. Make sure to follow the existing code style and add
|
147
|
+
tests if applicable.
|
148
|
+
|
149
|
+
---
|
150
|
+
|
151
|
+
## License
|
152
|
+
|
153
|
+
MIT License © Rajan Bhattarai
|
154
|
+
|
155
|
+
---
|
156
|
+
|
157
|
+
## Acknowledgments
|
158
|
+
|
159
|
+
- [Namecheap API](https://www.namecheap.com/support/api/)
|
160
|
+
- [TTY Toolkit](https://ttytoolkit.org/)
|
161
|
+
- [Artii](https://github.com/miketierney/artii)
|
162
|
+
- [Colorize](https://github.com/fazibear/colorize)
|
163
|
+
|
164
|
+
---
|
data/bin/brandkit
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
|
5
|
+
module ApiClients
|
6
|
+
# A client for interacting with the Namecheap XML API.
|
7
|
+
# Currently supports checking domain availability.
|
8
|
+
class NamecheapClient
|
9
|
+
include HTTParty
|
10
|
+
base_uri 'https://api.namecheap.com/xml.response'
|
11
|
+
|
12
|
+
attr_reader :api_user, :api_key, :username, :client_ip
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@api_user = ENV.fetch('NAMECHEAP_API_USER')
|
16
|
+
@api_key = ENV.fetch('NAMECHEAP_API_KEY')
|
17
|
+
@username = ENV.fetch('NAMECHEAP_USERNAME')
|
18
|
+
@client_ip = ENV.fetch('CLIENT_IP')
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Check if a domain is available for registration.
|
22
|
+
#
|
23
|
+
# domain - A String domain name to check (e.g., "example.com").
|
24
|
+
#
|
25
|
+
# Returns true if the domain is available, false otherwise.
|
26
|
+
def check_domain(domain)
|
27
|
+
response = make_request(
|
28
|
+
command: 'namecheap.domains.check',
|
29
|
+
params: { DomainList: domain }
|
30
|
+
)
|
31
|
+
|
32
|
+
available = response.dig('ApiResponse', 'CommandResponse', 'DomainCheckResult', 'Available')
|
33
|
+
available == 'true'
|
34
|
+
rescue StandardError => e
|
35
|
+
warn "Error checking domain '#{domain}': #{e.message}"
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Private: Build the query parameters for the API call.
|
42
|
+
#
|
43
|
+
# command - Namecheap API command string.
|
44
|
+
# params - Additional parameters for the specific command.
|
45
|
+
#
|
46
|
+
# Returns a Hash with all query parameters merged.
|
47
|
+
def build_query(command:, params: {})
|
48
|
+
{
|
49
|
+
ApiUser: api_user,
|
50
|
+
ApiKey: api_key,
|
51
|
+
UserName: username,
|
52
|
+
ClientIp: client_ip,
|
53
|
+
Command: command
|
54
|
+
}.merge(params)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Private: Perform the HTTP GET request to the Namecheap API.
|
58
|
+
#
|
59
|
+
# command - The API command to call (e.g., "namecheap.domains.check").
|
60
|
+
# params - Additional parameters for the command.
|
61
|
+
#
|
62
|
+
# Returns a parsed Hash of the XML response.
|
63
|
+
def make_request(command:, params: {})
|
64
|
+
query = build_query(command: command, params: params)
|
65
|
+
response = self.class.get('/', query: query)
|
66
|
+
|
67
|
+
raise "API call failed with HTTP #{response.code}" unless response.success?
|
68
|
+
|
69
|
+
parsed = response.parsed_response
|
70
|
+
raise 'Invalid API response structure' unless parsed.is_a?(Hash)
|
71
|
+
|
72
|
+
parsed
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/brand_kit.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'byebug'
|
4
|
+
require 'tty-prompt'
|
5
|
+
require 'tty-table'
|
6
|
+
require 'colorize'
|
7
|
+
require 'artii'
|
8
|
+
require 'dotenv/load' # this auto-loads .env
|
9
|
+
|
10
|
+
require_relative 'domain_checker'
|
11
|
+
require_relative 'social_username_checker'
|
12
|
+
require_relative 'api_clients/namecheap_client'
|
13
|
+
|
14
|
+
# BrandKit CLI app for checking domain and social media username availability
|
15
|
+
class BrandKit
|
16
|
+
# Initialize the CLI with prompt, ASCII art generator, and JSON output flag
|
17
|
+
def initialize
|
18
|
+
@prompt = TTY::Prompt.new
|
19
|
+
@artii = Artii::Base.new
|
20
|
+
@json_output = ARGV.include?('--json')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Main entry point to run the app flow:
|
24
|
+
# 1. Show intro banner
|
25
|
+
# 2. Get domain input from args or prompt
|
26
|
+
# 3. Check domain availability
|
27
|
+
# 4. Show domain result with purchase link if available
|
28
|
+
# 5. Optionally check social username availability
|
29
|
+
# 6. Show farewell banner
|
30
|
+
def run
|
31
|
+
intro_banner
|
32
|
+
|
33
|
+
domain = extract_domain_from_argv || prompt_for_domain
|
34
|
+
domain_result = DomainChecker.new(domain).check
|
35
|
+
|
36
|
+
show_result_box(domain_result[:message], :green)
|
37
|
+
|
38
|
+
print_purchase_link(domain_result) if domain_result[:link]
|
39
|
+
|
40
|
+
ask_social_check(domain_result[:domain])
|
41
|
+
farewell_banner
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Extract domain argument from ARGV (e.g. --domain=example.com)
|
47
|
+
#
|
48
|
+
# Returns domain string or nil if not present
|
49
|
+
def extract_domain_from_argv
|
50
|
+
domain_arg = ARGV.find { |arg| arg.start_with?('--domain=') }
|
51
|
+
domain_arg&.split('=', 2)&.last
|
52
|
+
end
|
53
|
+
|
54
|
+
# Prompt the user for a domain input with colored prompt message
|
55
|
+
#
|
56
|
+
# Returns the user-entered domain string
|
57
|
+
def prompt_for_domain
|
58
|
+
colored_prompt('🌐 Enter domain (e.g. example or example.com):')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Show ASCII art intro banner with app title and tagline
|
62
|
+
def intro_banner
|
63
|
+
ascii = @artii.asciify('BrandKit').colorize(:cyan)
|
64
|
+
puts ascii
|
65
|
+
|
66
|
+
width = 60
|
67
|
+
title = '🚀 Welcome to BrandKit'
|
68
|
+
top_border = " #{title.ljust(width - 2)}"
|
69
|
+
bottom_border = '─' * width
|
70
|
+
|
71
|
+
body = [
|
72
|
+
'',
|
73
|
+
'The fastest way to check domain and',
|
74
|
+
'social media username availability!',
|
75
|
+
''
|
76
|
+
].map { |line| line.colorize(:light_white).center(width) }
|
77
|
+
|
78
|
+
puts top_border
|
79
|
+
body.each { |line| puts line }
|
80
|
+
puts bottom_border
|
81
|
+
end
|
82
|
+
|
83
|
+
# Show farewell banner with thanks message
|
84
|
+
def farewell_banner
|
85
|
+
width = 60
|
86
|
+
title = '👋'
|
87
|
+
top_border = " #{title.ljust(width - 2)}"
|
88
|
+
bottom_border = '─' * width
|
89
|
+
|
90
|
+
body = [
|
91
|
+
'',
|
92
|
+
'Thanks for using BrandKit!',
|
93
|
+
'Start building your brand today. ✨',
|
94
|
+
''
|
95
|
+
].map { |line| line.colorize(:light_magenta).center(width) }
|
96
|
+
|
97
|
+
puts top_border
|
98
|
+
body.each { |line| puts line }
|
99
|
+
puts bottom_border
|
100
|
+
end
|
101
|
+
|
102
|
+
# Display a formatted box with a status message in specified color
|
103
|
+
#
|
104
|
+
# message - String message to display
|
105
|
+
# color - Symbol colorize color (default :green)
|
106
|
+
def show_result_box(message, color = :green)
|
107
|
+
return if message.to_s.strip.empty?
|
108
|
+
|
109
|
+
width = 60
|
110
|
+
label = '✦ Domain Status'
|
111
|
+
top_border = " #{label.ljust(width - 2)}"
|
112
|
+
bottom_border = '─' * width
|
113
|
+
|
114
|
+
body = ['', message.colorize(:light_green).center(width), '']
|
115
|
+
|
116
|
+
puts "\n"
|
117
|
+
puts top_border.colorize(color)
|
118
|
+
body.each { |line| puts line }
|
119
|
+
puts bottom_border.colorize(color)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Prompt with colorized question and return the answer
|
123
|
+
#
|
124
|
+
# question - String question to ask user
|
125
|
+
#
|
126
|
+
# Returns user input string
|
127
|
+
def colored_prompt(question)
|
128
|
+
@prompt.ask(question.colorize(:light_blue))
|
129
|
+
end
|
130
|
+
|
131
|
+
# Print domain purchase link if domain is available
|
132
|
+
#
|
133
|
+
# domain_result - Hash containing domain info (expects :domain key)
|
134
|
+
def print_purchase_link(domain_result)
|
135
|
+
purchase_url = "https://www.namecheap.com/domains/registration/results/?domain=#{domain_result[:domain]}"
|
136
|
+
puts "\n🔗 #{'Purchase here:'.colorize(:light_green)} #{purchase_url.underline}"
|
137
|
+
puts "\n"
|
138
|
+
end
|
139
|
+
|
140
|
+
# Prompt user whether to check social username availability,
|
141
|
+
# allow selection of platforms, check availability, and print results in a table
|
142
|
+
#
|
143
|
+
# domain - String domain name to check usernames for
|
144
|
+
def ask_social_check(domain)
|
145
|
+
return unless @prompt.yes?('📱 Check if the username is available on social platforms?'.colorize(:light_cyan))
|
146
|
+
|
147
|
+
all_choice = '🌍 All supported platforms'
|
148
|
+
choices = [all_choice] + SocialUsernameChecker::PLATFORMS.keys.map(&:to_s)
|
149
|
+
|
150
|
+
selected = prompt_platform_selection(choices, all_choice)
|
151
|
+
|
152
|
+
platforms_to_check = if selected.include?(all_choice)
|
153
|
+
SocialUsernameChecker::PLATFORMS.keys
|
154
|
+
else
|
155
|
+
selected.map(&:to_sym)
|
156
|
+
end
|
157
|
+
|
158
|
+
checker = SocialUsernameChecker.new
|
159
|
+
username = checker.send(:strip_extension, domain)
|
160
|
+
|
161
|
+
results = platforms_to_check.map do |platform|
|
162
|
+
available = checker.username_available?(username, platform)
|
163
|
+
status = available ? '✔ Available'.colorize(:green) : '✘ Taken'.colorize(:red)
|
164
|
+
[platform.to_s.capitalize, status]
|
165
|
+
end
|
166
|
+
|
167
|
+
table = TTY::Table.new(['📡 Platform', '🔍 Status'], results)
|
168
|
+
puts "\n#{table.render(:unicode, padding: [0, 3], alignment: [:center])}"
|
169
|
+
end
|
170
|
+
|
171
|
+
# Loop prompting user to select platforms, enforcing selection rules
|
172
|
+
#
|
173
|
+
# choices - Array of choice strings including 'All supported platforms'
|
174
|
+
# all_choice - String representing 'all platforms' option
|
175
|
+
#
|
176
|
+
# Returns array of selected platform strings
|
177
|
+
def prompt_platform_selection(choices, all_choice)
|
178
|
+
selected = []
|
179
|
+
|
180
|
+
loop do
|
181
|
+
puts "\n"
|
182
|
+
selected = @prompt.multi_select('✔ Select platforms to check (space to select):'.colorize(:cyan), choices, per_page: 12)
|
183
|
+
|
184
|
+
if selected.empty?
|
185
|
+
puts '⚠️ You must select at least one platform.'.colorize(:yellow)
|
186
|
+
elsif selected.include?(all_choice) && selected.length > 1
|
187
|
+
puts "⚠️ Please select either '#{all_choice}' or specific platforms, not both.".colorize(:yellow)
|
188
|
+
else
|
189
|
+
break
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
selected
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tty-table'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
# DomainChecker handles checking domain availability using Namecheap API.
|
7
|
+
# Supports both exact domain check and suggestions with common TLDs.
|
8
|
+
class DomainChecker
|
9
|
+
COMMON_TLDS = %w[.com .net .org .io .dev .app .co .xyz .tech .site].freeze
|
10
|
+
|
11
|
+
# Initialize with the domain string to check.
|
12
|
+
#
|
13
|
+
# domain - String domain or base name (e.g. "example" or "example.com")
|
14
|
+
def initialize(domain)
|
15
|
+
@domain = domain
|
16
|
+
@client = ApiClients::NamecheapClient.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# Perform the domain availability check.
|
20
|
+
#
|
21
|
+
# Returns a Hash with keys:
|
22
|
+
# - :available (Boolean)
|
23
|
+
# - :domain (String)
|
24
|
+
# - :message (String, colorized)
|
25
|
+
# - :link (String URL if available)
|
26
|
+
# - :suggestions (Array of [domain, available] for common TLDs if no exact domain)
|
27
|
+
def check
|
28
|
+
if exact_domain?(@domain)
|
29
|
+
check_exact_domain(@domain)
|
30
|
+
else
|
31
|
+
check_domain_suggestions(@domain)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Determine if the domain includes a dot (TLD)
|
38
|
+
#
|
39
|
+
# domain - String
|
40
|
+
#
|
41
|
+
# Returns Boolean
|
42
|
+
def exact_domain?(domain)
|
43
|
+
domain.include?('.')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check availability for exact domain (with TLD)
|
47
|
+
#
|
48
|
+
# domain - String
|
49
|
+
#
|
50
|
+
# Returns Hash with availability info and message
|
51
|
+
def check_exact_domain(domain)
|
52
|
+
available = @client.check_domain(domain)
|
53
|
+
|
54
|
+
{
|
55
|
+
available: available,
|
56
|
+
domain: domain,
|
57
|
+
message: available ? success_message(domain) : failure_message(domain),
|
58
|
+
link: available ? purchase_link(domain) : nil
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check availability for common TLD suggestions and display results
|
63
|
+
#
|
64
|
+
# base_domain - String domain base without TLD (e.g. "example")
|
65
|
+
#
|
66
|
+
# Returns Hash with message, suggestions, and availability false
|
67
|
+
def check_domain_suggestions(base_domain)
|
68
|
+
suggestions = COMMON_TLDS.map do |tld|
|
69
|
+
full_domain = "#{base_domain}#{tld}"
|
70
|
+
available = @client.check_domain(full_domain)
|
71
|
+
[full_domain, available]
|
72
|
+
end
|
73
|
+
|
74
|
+
print_suggestions_table(suggestions.first(10))
|
75
|
+
|
76
|
+
available_count = suggestions.count { |_, available| available }
|
77
|
+
|
78
|
+
{
|
79
|
+
available: false,
|
80
|
+
domain: base_domain,
|
81
|
+
message: availability_summary_message(base_domain, available_count),
|
82
|
+
suggestions: suggestions
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Print a formatted table of domain suggestions and their availability
|
87
|
+
#
|
88
|
+
# suggestions - Array of [domain, Boolean availability]
|
89
|
+
def print_suggestions_table(suggestions)
|
90
|
+
rows = suggestions.map do |domain, available|
|
91
|
+
[domain, available ? '✔ Available'.colorize(:green) : '✖ Taken'.colorize(:red)]
|
92
|
+
end
|
93
|
+
|
94
|
+
table = TTY::Table.new(%w[Domain Status], rows)
|
95
|
+
puts "\nTop Domain Extensions:\n"
|
96
|
+
puts table.render(:unicode, padding: [0, 2])
|
97
|
+
end
|
98
|
+
|
99
|
+
# Compose a colorized success message for available domain
|
100
|
+
#
|
101
|
+
# domain - String
|
102
|
+
#
|
103
|
+
# Returns String
|
104
|
+
def success_message(domain)
|
105
|
+
"✔ Domain #{domain} is available!".colorize(:green)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Compose a colorized failure message for taken domain
|
109
|
+
#
|
110
|
+
# domain - String
|
111
|
+
#
|
112
|
+
# Returns String
|
113
|
+
def failure_message(domain)
|
114
|
+
"✘ Domain #{domain} is taken.".colorize(:red)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Generate a purchase link URL for a domain
|
118
|
+
#
|
119
|
+
# domain - String
|
120
|
+
#
|
121
|
+
# Returns String URL
|
122
|
+
def purchase_link(domain)
|
123
|
+
"https://www.namecheap.com/domains/registration/results/?domain=#{domain}"
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return a user-friendly message summarizing availability across TLDs
|
127
|
+
#
|
128
|
+
# base_domain - String base domain (without TLD)
|
129
|
+
# available_count - Integer count of available domains
|
130
|
+
#
|
131
|
+
# Returns String message
|
132
|
+
def availability_summary_message(base_domain, available_count)
|
133
|
+
case available_count
|
134
|
+
when 0
|
135
|
+
"😞 No common domain extensions are available for ‘#{base_domain}’. Try a different name."
|
136
|
+
when 1..3
|
137
|
+
"⚠️ Only a few options are available for ‘#{base_domain}’. Consider securing one quickly!"
|
138
|
+
when 4..6
|
139
|
+
"🙂 Some good domain extensions are still available for ‘#{base_domain}’."
|
140
|
+
else
|
141
|
+
"🎉 Great news! Many domain extensions are available for ‘#{base_domain}’."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'colorize'
|
6
|
+
|
7
|
+
# SocialUsernameChecker checks username availability across multiple social platforms.
|
8
|
+
class SocialUsernameChecker
|
9
|
+
# Supported platforms and their URL patterns for usernames
|
10
|
+
PLATFORMS = {
|
11
|
+
github: 'https://github.com/%<username>s',
|
12
|
+
twitter: 'https://twitter.com/%<username>s',
|
13
|
+
instagram: 'https://www.instagram.com/%<username>s',
|
14
|
+
facebook: 'https://www.facebook.com/%<username>s',
|
15
|
+
youtube: 'https://www.youtube.com/@%<username>s',
|
16
|
+
tiktok: 'https://www.tiktok.com/@%<username>s',
|
17
|
+
pinterest: 'https://www.pinterest.com/%<username>s',
|
18
|
+
linkedin: 'https://www.linkedin.com/in/%<username>s',
|
19
|
+
reddit: 'https://www.reddit.com/user/%<username>s',
|
20
|
+
threads: 'https://www.threads.net/@%<username>s'
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
# Returns an array of supported platform names as strings
|
24
|
+
#
|
25
|
+
# Returns Array<String>
|
26
|
+
def supported_platforms
|
27
|
+
PLATFORMS.keys.map(&:to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if a username is available on a given platform.
|
31
|
+
#
|
32
|
+
# username - String username to check
|
33
|
+
# platform - Symbol or String platform key (e.g., :github or "github")
|
34
|
+
#
|
35
|
+
# Returns Boolean true if available, false if taken or on error.
|
36
|
+
def username_available?(username, platform)
|
37
|
+
platform_key = platform.to_sym
|
38
|
+
|
39
|
+
unless PLATFORMS.key?(platform_key)
|
40
|
+
warn "Unsupported platform: #{platform}"
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
|
44
|
+
url = format(PLATFORMS[platform_key], username: username)
|
45
|
+
uri = URI.parse(url)
|
46
|
+
|
47
|
+
response = fetch_response(uri)
|
48
|
+
|
49
|
+
# If HTTP status is not 200, assume username is available (profile not found)
|
50
|
+
return true unless response.code == '200'
|
51
|
+
|
52
|
+
# Check response body for username presence (basic heuristic)
|
53
|
+
body = response.body.force_encoding('UTF-8')
|
54
|
+
|
55
|
+
# If the username appears in the page content, it's likely taken
|
56
|
+
!body.include?(username)
|
57
|
+
rescue StandardError => e
|
58
|
+
warn "⚠️ Error checking #{platform}: #{e.message}".colorize(:yellow)
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Perform a GET request and return the HTTP response
|
65
|
+
#
|
66
|
+
# uri - URI object
|
67
|
+
#
|
68
|
+
# Returns Net::HTTPResponse
|
69
|
+
def fetch_response(uri)
|
70
|
+
Net::HTTP.get_response(uri)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Remove TLD extension from a domain to get a clean username base
|
74
|
+
#
|
75
|
+
# domain - String domain name (e.g. "example.com")
|
76
|
+
#
|
77
|
+
# Returns String username base (e.g. "example")
|
78
|
+
def strip_extension(domain)
|
79
|
+
domain.split('.').first
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: brandkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rajan Bhattarai
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-06-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.18'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.18'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-prompt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.23'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.23'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-table
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.12'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: artii
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '11.1'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '11.1'
|
111
|
+
description: BrandKit is a Ruby CLI that helps check domain availability via Namecheap
|
112
|
+
API and social media username availability across multiple platforms.
|
113
|
+
email:
|
114
|
+
- publisher@rajanbhattarai.com
|
115
|
+
executables:
|
116
|
+
- brandkit
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- LICENSE
|
121
|
+
- README.md
|
122
|
+
- bin/brandkit
|
123
|
+
- lib/api_clients/namecheap_client.rb
|
124
|
+
- lib/brand_kit.rb
|
125
|
+
- lib/domain_checker.rb
|
126
|
+
- lib/social_username_checker.rb
|
127
|
+
homepage: https://github.com/cdrrazan/brandkit
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata:
|
131
|
+
bug_tracker_uri: https://github.com/cdrrazan/brandkit/issues
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '3.2'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.4.17
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: A CLI tool to check domain and social username availability
|
151
|
+
test_files: []
|