wabot 0.1.0 → 0.1.2
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 +4 -4
- data/README.md +1 -2
- data/bin/wabot +1 -1
- data/lib/wabot/bot.rb +62 -3
- data/lib/wabot/cli.rb +3 -5
- data/lib/wabot/user_store.rb +4 -14
- data/lib/wabot/version.rb +1 -1
- metadata +2 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f07b179733062731ea76f1ffb30943949fe49d5d8343de859fd41b58c8f46494
|
|
4
|
+
data.tar.gz: 32b9957527498cda3b574358d424447b078fecf3f17c595b5a4195e301d09c1f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 624dfeb7730f1f1f316ae6b393ddd07ff968579b6c143c993872ba3ecb3542a3b6968e7c416bb44dd5fecea5cac741cb66f05e39537603e996d6131fcb1ec4f6
|
|
7
|
+
data.tar.gz: 9a92b158e50f703fcafa3c02b8c71c1b85142ca7bc9c4e939c27e405e9c43fbc7de6f1aec009a76aad996402f006b29c965c9aea9e529aba4f7581e93f9312bd
|
data/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Notes:
|
|
|
47
47
|
- Selenium Manager will handle downloading a compatible chromedriver automatically
|
|
48
48
|
|
|
49
49
|
## Security
|
|
50
|
-
- Local
|
|
50
|
+
- Local users are username-only (no passwords) and stored in `storage/users.json`
|
|
51
51
|
- The current CLI login session is stored in `storage/session.json`
|
|
52
52
|
- Your WhatsApp Web cookies/tokens live inside `profiles/<username>`; keep this folder private
|
|
53
53
|
|
|
@@ -61,7 +61,6 @@ Notes:
|
|
|
61
61
|
You can use this as a library in your own Ruby code. Build and install the gem locally:
|
|
62
62
|
|
|
63
63
|
```
|
|
64
|
-
cd wabot
|
|
65
64
|
gem build wabot.gemspec
|
|
66
65
|
gem install ./wabot-0.1.0.gem
|
|
67
66
|
```
|
data/bin/wabot
CHANGED
data/lib/wabot/bot.rb
CHANGED
|
@@ -58,6 +58,8 @@ module WaBot
|
|
|
58
58
|
encoded_text = URI.encode_www_form_component(message)
|
|
59
59
|
chat_url = "#{BASE_URL}/send?phone=#{digits_phone}&text=#{encoded_text}"
|
|
60
60
|
@driver.navigate.to(chat_url)
|
|
61
|
+
# Some dialogs (e.g., notifications prompts) can appear after navigation; try to close them.
|
|
62
|
+
dismiss_overlays
|
|
61
63
|
|
|
62
64
|
wait = Selenium::WebDriver::Wait.new(timeout: 60)
|
|
63
65
|
begin
|
|
@@ -67,7 +69,14 @@ module WaBot
|
|
|
67
69
|
# Always attempt to type the message explicitly to avoid relying on URL prefill
|
|
68
70
|
box = find_message_box
|
|
69
71
|
if box
|
|
70
|
-
|
|
72
|
+
begin
|
|
73
|
+
# Focus composer via JS to avoid click interception
|
|
74
|
+
@driver.execute_script("arguments[0].focus();", box)
|
|
75
|
+
rescue Selenium::WebDriver::Error::ElementClickInterceptedError
|
|
76
|
+
# If some overlay intercepted, try dismissing and continue
|
|
77
|
+
dismiss_overlays
|
|
78
|
+
@driver.execute_script("arguments[0].focus();", box)
|
|
79
|
+
end
|
|
71
80
|
sleep 0.15
|
|
72
81
|
# Select-all + delete to clear any previous text
|
|
73
82
|
modifier = (RUBY_PLATFORM =~ /darwin/i ? :command : :control)
|
|
@@ -83,7 +92,13 @@ module WaBot
|
|
|
83
92
|
begin
|
|
84
93
|
@driver.execute_script("arguments[0].click();", btn)
|
|
85
94
|
rescue
|
|
86
|
-
|
|
95
|
+
begin
|
|
96
|
+
btn.click
|
|
97
|
+
rescue Selenium::WebDriver::Error::ElementClickInterceptedError
|
|
98
|
+
# Dismiss overlays and retry JS click once
|
|
99
|
+
dismiss_overlays
|
|
100
|
+
@driver.execute_script("arguments[0].click();", btn)
|
|
101
|
+
end
|
|
87
102
|
end
|
|
88
103
|
else
|
|
89
104
|
# If no button, try Enter/Return inside the composer
|
|
@@ -156,7 +171,6 @@ module WaBot
|
|
|
156
171
|
"div[data-testid='conversation-compose-box-input']",
|
|
157
172
|
"div[contenteditable='true'][data-tab='10']",
|
|
158
173
|
"div[contenteditable='true'][data-tab='6']",
|
|
159
|
-
"div[role='textbox'][contenteditable='true']",
|
|
160
174
|
"div[aria-label='Type a message']",
|
|
161
175
|
"div[title='Type a message']"
|
|
162
176
|
]
|
|
@@ -210,5 +224,50 @@ module WaBot
|
|
|
210
224
|
false
|
|
211
225
|
end
|
|
212
226
|
end
|
|
227
|
+
|
|
228
|
+
def overlay_present?
|
|
229
|
+
begin
|
|
230
|
+
@driver.find_elements(css: "div[role='dialog']").any? { |el| el.displayed? }
|
|
231
|
+
rescue
|
|
232
|
+
false
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def dismiss_overlays
|
|
237
|
+
# Best-effort: press ESC, click common dismiss buttons, and wait briefly
|
|
238
|
+
begin
|
|
239
|
+
# Press ESC up to 2 times
|
|
240
|
+
body = @driver.find_element(tag_name: "body")
|
|
241
|
+
2.times { body.send_keys(:escape) }
|
|
242
|
+
rescue
|
|
243
|
+
end
|
|
244
|
+
# Try common close buttons
|
|
245
|
+
buttons_css = [
|
|
246
|
+
"div[role='dialog'] button[aria-label='Close']",
|
|
247
|
+
"div[role='dialog'] button[aria-label='close']",
|
|
248
|
+
"div[role='dialog'] button[title='Close']"
|
|
249
|
+
]
|
|
250
|
+
buttons_css.each do |css|
|
|
251
|
+
@driver.find_elements(css: css).each do |btn|
|
|
252
|
+
begin
|
|
253
|
+
@driver.execute_script("arguments[0].click();", btn)
|
|
254
|
+
rescue
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
# Try common text buttons
|
|
259
|
+
texts = ["Not now", "No thanks", "Cancel", "Close", "Got it", "OK"]
|
|
260
|
+
texts.each do |txt|
|
|
261
|
+
begin
|
|
262
|
+
el = @driver.find_element(xpath: "//div[@role='dialog']//button[normalize-space(text())='#{txt}']")
|
|
263
|
+
@driver.execute_script("arguments[0].click();", el)
|
|
264
|
+
rescue
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
# Small wait to allow overlay to disappear
|
|
268
|
+
Selenium::WebDriver::Wait.new(timeout: 3).until { !overlay_present? }
|
|
269
|
+
rescue
|
|
270
|
+
# ignore
|
|
271
|
+
end
|
|
213
272
|
end
|
|
214
273
|
end
|
data/lib/wabot/cli.rb
CHANGED
|
@@ -11,11 +11,10 @@ module WaBot
|
|
|
11
11
|
class CLI < Thor
|
|
12
12
|
desc "register", "Register a new local user"
|
|
13
13
|
method_option :username, aliases: "-u", type: :string, required: true, desc: "Username"
|
|
14
|
-
method_option :password, aliases: "-p", type: :string, required: true, desc: "Password"
|
|
15
14
|
def register
|
|
16
15
|
store = UserStore.new
|
|
17
16
|
begin
|
|
18
|
-
store.register(options[:username]
|
|
17
|
+
store.register(options[:username])
|
|
19
18
|
puts "User registered: #{options[:username]}".green
|
|
20
19
|
rescue => e
|
|
21
20
|
puts "Error: #{e.message}".red
|
|
@@ -25,11 +24,10 @@ module WaBot
|
|
|
25
24
|
|
|
26
25
|
desc "login", "Login as a local user"
|
|
27
26
|
method_option :username, aliases: "-u", type: :string, required: true
|
|
28
|
-
method_option :password, aliases: "-p", type: :string, required: true
|
|
29
27
|
def login
|
|
30
28
|
store = UserStore.new
|
|
31
|
-
unless store.authenticate(options[:username]
|
|
32
|
-
puts "
|
|
29
|
+
unless store.authenticate(options[:username])
|
|
30
|
+
puts "User not found. Please register first.".red
|
|
33
31
|
exit 1
|
|
34
32
|
end
|
|
35
33
|
session = SessionStore.new
|
data/lib/wabot/user_store.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
-
require "bcrypt"
|
|
5
4
|
require "fileutils"
|
|
6
5
|
|
|
7
6
|
module WaBot
|
|
@@ -13,29 +12,20 @@ module WaBot
|
|
|
13
12
|
initialize_file
|
|
14
13
|
end
|
|
15
14
|
|
|
16
|
-
def register(username
|
|
15
|
+
def register(username)
|
|
17
16
|
raise "Username is required" if username.to_s.strip.empty?
|
|
18
|
-
raise "Password is required" if password.to_s.strip.empty?
|
|
19
17
|
|
|
20
18
|
users = read_users
|
|
21
19
|
raise "User already exists" if users.any? { |u| u["username"] == username }
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
users << { "username" => username, "password_hash" => password_hash }
|
|
21
|
+
users << { "username" => username }
|
|
25
22
|
write_users(users)
|
|
26
23
|
true
|
|
27
24
|
end
|
|
28
25
|
|
|
29
|
-
def authenticate(username
|
|
26
|
+
def authenticate(username)
|
|
30
27
|
users = read_users
|
|
31
|
-
|
|
32
|
-
return false unless user
|
|
33
|
-
|
|
34
|
-
begin
|
|
35
|
-
BCrypt::Password.new(user["password_hash"]) == password
|
|
36
|
-
rescue
|
|
37
|
-
false
|
|
38
|
-
end
|
|
28
|
+
users.any? { |u| u["username"] == username }
|
|
39
29
|
end
|
|
40
30
|
|
|
41
31
|
def users
|
data/lib/wabot/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wabot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vikas Kumar
|
|
@@ -37,20 +37,6 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1.3'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: bcrypt
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '3.1'
|
|
47
|
-
type: :runtime
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - "~>"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '3.1'
|
|
54
40
|
- !ruby/object:Gem::Dependency
|
|
55
41
|
name: colorize
|
|
56
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -82,7 +68,7 @@ files:
|
|
|
82
68
|
- lib/wabot/session_store.rb
|
|
83
69
|
- lib/wabot/user_store.rb
|
|
84
70
|
- lib/wabot/version.rb
|
|
85
|
-
homepage: https://github.com/vikas-0/
|
|
71
|
+
homepage: https://github.com/vikas-0/wabot
|
|
86
72
|
licenses:
|
|
87
73
|
- MIT
|
|
88
74
|
metadata: {}
|