@gtchakama/wa-tui 1.0.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.
- package/README.md +79 -0
- package/bin/wa-tui.js +3 -0
- package/package.json +49 -0
- package/src/config/palettes.js +87 -0
- package/src/config/userSettings.js +35 -0
- package/src/index.js +26 -0
- package/src/ui/renderer.js +1095 -0
- package/src/ui/state.js +30 -0
- package/src/utils/format.js +54 -0
- package/src/utils/messageFormat.js +53 -0
- package/src/utils/notifySound.js +72 -0
- package/src/utils/pager.js +19 -0
- package/src/whatsapp/service.js +264 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# wa-tui
|
|
2
|
+
|
|
3
|
+
Terminal UI for WhatsApp Web, built with [neo-blessed](https://www.npmjs.com/package/neo-blessed) and [whatsapp-web.js](https://wwebjs.dev/). Scan a QR code in the terminal, then browse chats, read messages, reply, and download media without leaving your shell.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Node.js** 18 or newer
|
|
8
|
+
- **Google Chrome** or **Chromium** installed locally — `whatsapp-web.js` drives a headless browser session; if startup fails, install Chrome or point Puppeteer at your binary (see [whatsapp-web.js docs](https://docs.wwebjs.dev/)).
|
|
9
|
+
|
|
10
|
+
Session data and cache are stored under `.wwebjs_auth` and `.wwebjs_cache` in the current working directory (and settings under `~/.wa-tui/`).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @gtchakama/wa-tui
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or run without a global install:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx @gtchakama/wa-tui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
From a git checkout:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install
|
|
28
|
+
npm start
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
wa-tui
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
On first run, scan the QR code shown in the terminal with WhatsApp on your phone (**Linked devices**).
|
|
38
|
+
|
|
39
|
+
### Environment variables
|
|
40
|
+
|
|
41
|
+
| Variable | Description |
|
|
42
|
+
| -------- | ----------- |
|
|
43
|
+
| `WA_TUI_RESIZE` | Set to `1` to enable resize-related behavior used during development (`npm run start:resize`). |
|
|
44
|
+
| `WA_TUI_NO_SOUND` | Set to `1` to disable the incoming-message notification sound. |
|
|
45
|
+
| `WA_TUI_SOUND` | Optional path to an audio file (macOS: `afplay`; Linux: `paplay` / `aplay`). Overrides the default tone. |
|
|
46
|
+
|
|
47
|
+
Incoming messages play a short sound when you are **not** viewing that chat. Defaults: **macOS** — `Ping.aiff` via `afplay`; **Windows** — short two-tone console beep; **Linux** — freedesktop `complete.oga` or `message.oga`, then WAV fallback; otherwise the terminal bell. There is no sound for your own messages or for messages in the chat you currently have open.
|
|
48
|
+
|
|
49
|
+
## Scripts (development)
|
|
50
|
+
|
|
51
|
+
| Command | Description |
|
|
52
|
+
| ------- | ----------- |
|
|
53
|
+
| `npm start` | Run the TUI |
|
|
54
|
+
| `npm run start:resize` | Run with `WA_TUI_RESIZE=1` |
|
|
55
|
+
|
|
56
|
+
## Publishing
|
|
57
|
+
|
|
58
|
+
This package lists only the `bin`, `src`, and `README.md` files when published (`files` field in `package.json`). To try a pack locally:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm pack
|
|
62
|
+
tar -tzvf gtchakama-wa-tui-*.tgz
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm publish --access public
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Scoped packages must use `--access public` for a free public listing on the npm registry.
|
|
72
|
+
|
|
73
|
+
## Disclaimer
|
|
74
|
+
|
|
75
|
+
WhatsApp’s terms of service apply to any client you use. This project is an unofficial interface; use it at your own risk.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
ISC
|
package/bin/wa-tui.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gtchakama/wa-tui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Terminal UI for WhatsApp Web (blessed + whatsapp-web.js)",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wa-tui": "bin/wa-tui.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node src/index.js",
|
|
16
|
+
"start:resize": "WA_TUI_RESIZE=1 node src/index.js",
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/gtchakama/wa-tui.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"whatsapp",
|
|
25
|
+
"whatsapp-web",
|
|
26
|
+
"tui",
|
|
27
|
+
"terminal",
|
|
28
|
+
"cli",
|
|
29
|
+
"blessed",
|
|
30
|
+
"chat"
|
|
31
|
+
],
|
|
32
|
+
"author": "gtchakama",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"type": "commonjs",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/gtchakama/wa-tui/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/gtchakama/wa-tui#readme",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"chromium-bidi": "^15.0.0",
|
|
44
|
+
"neo-blessed": "^0.2.0",
|
|
45
|
+
"puppeteer-core": "^24.40.0",
|
|
46
|
+
"qrcode-terminal": "^0.12.0",
|
|
47
|
+
"whatsapp-web.js": "^1.34.6"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Named colour palettes for the TUI (hex strings, blessed-safe). Foreground-only — terminal background comes from the host (e.g. VS Code theme). */
|
|
2
|
+
|
|
3
|
+
const PALETTES = {
|
|
4
|
+
ocean: {
|
|
5
|
+
label: 'Ocean (default)',
|
|
6
|
+
fg: '#68C5DB',
|
|
7
|
+
fgDim: '#448FA3',
|
|
8
|
+
accent: '#0197F6',
|
|
9
|
+
selfMsg: '#68C5DB',
|
|
10
|
+
peerMsg: '#448FA3',
|
|
11
|
+
error: '#D7263D',
|
|
12
|
+
unread: '#D7263D'
|
|
13
|
+
},
|
|
14
|
+
matrix: {
|
|
15
|
+
label: 'Matrix',
|
|
16
|
+
fg: '#3dff7a',
|
|
17
|
+
fgDim: '#2a8f4a',
|
|
18
|
+
accent: '#00ff66',
|
|
19
|
+
selfMsg: '#7dff9a',
|
|
20
|
+
peerMsg: '#5fcc7a',
|
|
21
|
+
error: '#ff3333',
|
|
22
|
+
unread: '#ff4444'
|
|
23
|
+
},
|
|
24
|
+
amber: {
|
|
25
|
+
label: 'Amber terminal',
|
|
26
|
+
fg: '#eebb6d',
|
|
27
|
+
fgDim: '#9a7a4a',
|
|
28
|
+
accent: '#ffb020',
|
|
29
|
+
selfMsg: '#ffcc77',
|
|
30
|
+
peerMsg: '#c9a060',
|
|
31
|
+
error: '#e05040',
|
|
32
|
+
unread: '#ff6644'
|
|
33
|
+
},
|
|
34
|
+
nord: {
|
|
35
|
+
label: 'Nord frost',
|
|
36
|
+
fg: '#eceff4',
|
|
37
|
+
fgDim: '#88c0d0',
|
|
38
|
+
accent: '#81a1c1',
|
|
39
|
+
selfMsg: '#8fbcbb',
|
|
40
|
+
peerMsg: '#d8dee9',
|
|
41
|
+
error: '#bf616a',
|
|
42
|
+
unread: '#bf616a'
|
|
43
|
+
},
|
|
44
|
+
paper: {
|
|
45
|
+
label: 'Paper (light)',
|
|
46
|
+
fg: '#2c2824',
|
|
47
|
+
fgDim: '#6b6560',
|
|
48
|
+
accent: '#2563eb',
|
|
49
|
+
selfMsg: '#1d6b4a',
|
|
50
|
+
peerMsg: '#4a5568',
|
|
51
|
+
error: '#b91c1c',
|
|
52
|
+
unread: '#b91c1c'
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Stable order in the settings picker. */
|
|
57
|
+
const PALETTE_ORDER = ['ocean', 'matrix', 'amber', 'nord', 'paper'];
|
|
58
|
+
|
|
59
|
+
const DEFAULT_PALETTE_ID = 'ocean';
|
|
60
|
+
|
|
61
|
+
function normalizePaletteId(id) {
|
|
62
|
+
if (id && typeof id === 'string' && PALETTES[id]) return id;
|
|
63
|
+
return DEFAULT_PALETTE_ID;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildTheme(paletteId) {
|
|
67
|
+
const id = normalizePaletteId(paletteId);
|
|
68
|
+
const p = PALETTES[id];
|
|
69
|
+
return {
|
|
70
|
+
paletteId: id,
|
|
71
|
+
fg: p.fg,
|
|
72
|
+
fgDim: p.fgDim,
|
|
73
|
+
accent: p.accent,
|
|
74
|
+
selfMsg: p.selfMsg,
|
|
75
|
+
peerMsg: p.peerMsg,
|
|
76
|
+
error: p.error,
|
|
77
|
+
unread: p.unread
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
PALETTES,
|
|
83
|
+
PALETTE_ORDER,
|
|
84
|
+
DEFAULT_PALETTE_ID,
|
|
85
|
+
normalizePaletteId,
|
|
86
|
+
buildTheme
|
|
87
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const SETTINGS_DIR = path.join(os.homedir(), '.wa-tui');
|
|
6
|
+
const SETTINGS_PATH = path.join(SETTINGS_DIR, 'settings.json');
|
|
7
|
+
|
|
8
|
+
function loadSettings() {
|
|
9
|
+
try {
|
|
10
|
+
const raw = fs.readFileSync(SETTINGS_PATH, 'utf8');
|
|
11
|
+
const data = JSON.parse(raw);
|
|
12
|
+
return data && typeof data === 'object' ? data : {};
|
|
13
|
+
} catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function saveSettings(partial) {
|
|
19
|
+
try {
|
|
20
|
+
fs.mkdirSync(SETTINGS_DIR, { recursive: true });
|
|
21
|
+
const prev = loadSettings();
|
|
22
|
+
const next = { ...prev, ...partial };
|
|
23
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(next, null, 2), 'utf8');
|
|
24
|
+
return next;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error('wa-tui: could not save settings', e.message);
|
|
27
|
+
return loadSettings();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
SETTINGS_PATH,
|
|
33
|
+
loadSettings,
|
|
34
|
+
saveSettings
|
|
35
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const waService = require('./whatsapp/service');
|
|
2
|
+
const renderer = require('./ui/renderer');
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
renderer.init();
|
|
6
|
+
|
|
7
|
+
await waService.initialize(
|
|
8
|
+
// onQr
|
|
9
|
+
(qr) => {
|
|
10
|
+
renderer.showQr(qr);
|
|
11
|
+
},
|
|
12
|
+
// onReady
|
|
13
|
+
() => {
|
|
14
|
+
renderer.handleReady();
|
|
15
|
+
},
|
|
16
|
+
// onAuth
|
|
17
|
+
() => {
|
|
18
|
+
console.log('Authenticated!');
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
main().catch(err => {
|
|
24
|
+
console.error('Fatal Error:', err);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|