levix-bot 2.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/LICENSE +21 -0
- package/README.md +105 -0
- package/SETUP.md +242 -0
- package/app.cjs +337 -0
- package/bin/levix.js +7 -0
- package/deploy/install.sh +182 -0
- package/deploy/levix.service +56 -0
- package/deploy/nginx/levix.leviro.net.conf +79 -0
- package/package.json +78 -0
- package/public/brand/apple-touch-icon.png +0 -0
- package/public/brand/banner.jpg +0 -0
- package/public/brand/banner.webp +0 -0
- package/public/brand/icon-16.png +0 -0
- package/public/brand/icon-192.png +0 -0
- package/public/brand/icon-32.png +0 -0
- package/public/brand/icon-512.png +0 -0
- package/public/brand/icon-512.webp +0 -0
- package/public/brand/mark.png +0 -0
- package/public/brand/mark.svg +16 -0
- package/public/brand/mark.webp +0 -0
- package/public/brand/wordmark.png +0 -0
- package/public/brand/wordmark.webp +0 -0
- package/public/dashboard.css +881 -0
- package/public/dashboard.js +1057 -0
- package/public/logo.svg +16 -0
- package/public/qrcode.min.js +1 -0
- package/public/socket.io.min.js +7 -0
- package/scheduler.cjs +197 -0
- package/src/auth/auth-storage.cjs +74 -0
- package/src/auth/use-database-auth-state.js +130 -0
- package/src/bootstrap/core.js +74 -0
- package/src/bootstrap/events.cjs +43 -0
- package/src/bootstrap/panel.js +97 -0
- package/src/cli.js +153 -0
- package/src/commands/autoschedule.cjs +84 -0
- package/src/commands/blacklist.cjs +116 -0
- package/src/commands/block.cjs +70 -0
- package/src/commands/calc.cjs +387 -0
- package/src/commands/debt.cjs +175 -0
- package/src/commands/deleteschedule.cjs +36 -0
- package/src/commands/gemini.cjs +727 -0
- package/src/commands/group/add.cjs +136 -0
- package/src/commands/group/all.cjs +43 -0
- package/src/commands/group/antiSpam.cjs +96 -0
- package/src/commands/group/antilink.cjs +167 -0
- package/src/commands/group/approveall.cjs +54 -0
- package/src/commands/group/clearwarns.cjs +41 -0
- package/src/commands/group/deletenote.cjs +33 -0
- package/src/commands/group/demote.cjs +62 -0
- package/src/commands/group/kick.cjs +85 -0
- package/src/commands/group/media.cjs +120 -0
- package/src/commands/group/members.cjs +59 -0
- package/src/commands/group/note.cjs +32 -0
- package/src/commands/group/promote.cjs +60 -0
- package/src/commands/group/removeall.cjs +42 -0
- package/src/commands/group/rules.cjs +31 -0
- package/src/commands/group/save.cjs +36 -0
- package/src/commands/group/setname.cjs +41 -0
- package/src/commands/group/setpp.cjs +51 -0
- package/src/commands/group/setrules.cjs +46 -0
- package/src/commands/group/setwarn.cjs +58 -0
- package/src/commands/group/tagadmins.cjs +50 -0
- package/src/commands/group/warn.cjs +105 -0
- package/src/commands/group/warns.cjs +66 -0
- package/src/commands/group/welcome.cjs +109 -0
- package/src/commands/group.cjs +160 -0
- package/src/commands/help.cjs +230 -0
- package/src/commands/listschedules.cjs +47 -0
- package/src/commands/loop.cjs +163 -0
- package/src/commands/memory.cjs +201 -0
- package/src/commands/mod.cjs +155 -0
- package/src/commands/notes.cjs +28 -0
- package/src/commands/perm.cjs +236 -0
- package/src/commands/ping.cjs +17 -0
- package/src/commands/poll.cjs +80 -0
- package/src/commands/prayer.cjs +64 -0
- package/src/commands/qr.cjs +39 -0
- package/src/commands/rand.cjs +130 -0
- package/src/commands/restart.cjs +25 -0
- package/src/commands/schedule.cjs +86 -0
- package/src/commands/score.cjs +130 -0
- package/src/commands/setprefix.cjs +40 -0
- package/src/commands/shortlink.cjs +74 -0
- package/src/commands/shutdown.cjs +45 -0
- package/src/commands/status.cjs +75 -0
- package/src/commands/stt.cjs +150 -0
- package/src/commands/todo.cjs +88 -0
- package/src/commands/tts.cjs +170 -0
- package/src/commands/unblock.cjs +57 -0
- package/src/commands/weather.cjs +79 -0
- package/src/config/ai-persona.md +17 -0
- package/src/config/baileys.config.js +51 -0
- package/src/config/brand.cjs +36 -0
- package/src/config/brand.esm.js +17 -0
- package/src/config/constants.js +16 -0
- package/src/config/defaults.cjs +91 -0
- package/src/config/lock.cjs +110 -0
- package/src/config/paths.cjs +90 -0
- package/src/config/runtime-config.cjs +219 -0
- package/src/config/secrets.cjs +130 -0
- package/src/config/settings.cjs +386 -0
- package/src/core/connection.js +199 -0
- package/src/core/events.js +61 -0
- package/src/core/socket.js +64 -0
- package/src/db/db.cjs +282 -0
- package/src/db/store.cjs +837 -0
- package/src/db/store.esm.js +17 -0
- package/src/domain/caddy.js +120 -0
- package/src/domain/command.js +380 -0
- package/src/domain/detect.js +173 -0
- package/src/domain/nginx.js +138 -0
- package/src/domain/system.js +148 -0
- package/src/handlers/command.handler.js +414 -0
- package/src/handlers/group.handler.js +73 -0
- package/src/handlers/message.handler.js +149 -0
- package/src/index.js +217 -0
- package/src/middleware/antispam.middleware.js +68 -0
- package/src/middleware/blacklist.middleware.js +29 -0
- package/src/middleware/forward-tracking.middleware.js +49 -0
- package/src/middleware/permissions.middleware.js +114 -0
- package/src/routes/dashboard.api.esm.js +779 -0
- package/src/services/aiAgent.cjs +317 -0
- package/src/services/aiTools.cjs +635 -0
- package/src/utils/datetime.cjs +61 -0
- package/src/utils/lid-helper.esm.js +143 -0
- package/src/utils/logger.cjs +72 -0
- package/src/utils/memory.cjs +385 -0
- package/src/utils/normalizeJid.cjs +65 -0
- package/src/utils/normalizeJid.esm.js +37 -0
- package/src/utils/openBrowser.cjs +129 -0
- package/src/utils/permissions.cjs +154 -0
- package/src/utils/permissions.esm.js +375 -0
- package/src/utils/recentMessageCache.esm.js +70 -0
- package/src/utils/requestOrigin.cjs +102 -0
- package/src/utils/sendBotMessage.cjs +43 -0
- package/src/utils/sendBotMessage.esm.js +139 -0
- package/src/utils/statusMessage.cjs +179 -0
- package/src/utils/storage-hub.cjs +49 -0
- package/src/utils/storage-hub.esm.js +24 -0
- package/src/utils/storage.cjs +80 -0
- package/src/utils/storage.esm.js +88 -0
- package/src/utils/textDecode.cjs +197 -0
- package/src/utils/thumbnail.cjs +591 -0
- package/src/utils/typing.esm.js +102 -0
- package/views/dashboard.ejs +329 -0
- package/views/login.ejs +105 -0
- package/views/qr.ejs +108 -0
- package/views/setup.ejs +122 -0
package/bin/levix.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install Levix on a Linux server and run it as a service.
|
|
3
|
+
#
|
|
4
|
+
# curl -fsSL https://levix.leviro.net/install.sh | bash
|
|
5
|
+
#
|
|
6
|
+
# This file is the source of every public installer. The copy served from
|
|
7
|
+
# levix.leviro.net/install/vX.Y.Z.sh is this same script with VERSION rewritten
|
|
8
|
+
# to that release, which is why a pinned URL keeps installing that version
|
|
9
|
+
# forever. The copy in git leaves VERSION as "latest".
|
|
10
|
+
#
|
|
11
|
+
# What it does, in order — read it before you pipe it into a shell, as you
|
|
12
|
+
# should with any script that asks for sudo:
|
|
13
|
+
#
|
|
14
|
+
# 1. checks for Node 24+ and stops with instructions if it isn't there
|
|
15
|
+
# 2. installs the `levix` command globally from the `levix-bot` npm package
|
|
16
|
+
# 3. creates a `levix` system user and /var/lib/levix for its data
|
|
17
|
+
# 4. installs the systemd unit, pointed at the levix it just installed
|
|
18
|
+
# 5. enables it, starts it, and prints where to go next
|
|
19
|
+
# 6. offers — but never assumes — to connect a domain
|
|
20
|
+
#
|
|
21
|
+
# It writes exactly three things outside npm's own prefix: the system user,
|
|
22
|
+
# /var/lib/levix, and /etc/systemd/system/levix.service. It never touches an
|
|
23
|
+
# existing installation's data, and it can be re-run to upgrade.
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
PACKAGE="levix-bot"
|
|
28
|
+
# Rewritten by scripts/build-release-installer.mjs when a release is published.
|
|
29
|
+
# Keep this line exactly as it is: one assignment, one double-quoted value.
|
|
30
|
+
VERSION="latest"
|
|
31
|
+
SERVICE="levix"
|
|
32
|
+
DATA_DIR="/var/lib/levix"
|
|
33
|
+
UNIT="/etc/systemd/system/${SERVICE}.service"
|
|
34
|
+
MIN_NODE_MAJOR=24
|
|
35
|
+
PORT=3001
|
|
36
|
+
|
|
37
|
+
say() { printf '\n %s\n' "$*"; }
|
|
38
|
+
die() { printf '\n error: %s\n\n' "$*" >&2; exit 1; }
|
|
39
|
+
|
|
40
|
+
sudo_if_needed() {
|
|
41
|
+
if [ "$(id -u)" -eq 0 ]; then
|
|
42
|
+
"$@"
|
|
43
|
+
else
|
|
44
|
+
command -v sudo >/dev/null 2>&1 || die "This needs root, and sudo is not installed."
|
|
45
|
+
sudo "$@"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# --- 1. Node ---------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
command -v node >/dev/null 2>&1 ||
|
|
52
|
+
die "Node is not installed. Get Node ${MIN_NODE_MAJOR} LTS from https://nodejs.org"
|
|
53
|
+
|
|
54
|
+
NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]')"
|
|
55
|
+
if [ "$NODE_MAJOR" -lt "$MIN_NODE_MAJOR" ]; then
|
|
56
|
+
die "Node ${MIN_NODE_MAJOR}+ is required (found $(node -v)). Levix stores everything in SQLite, which Node only ships with itself from ${MIN_NODE_MAJOR} on."
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
command -v npm >/dev/null 2>&1 || die "npm is not installed."
|
|
60
|
+
command -v systemctl >/dev/null 2>&1 ||
|
|
61
|
+
die "This script installs a systemd service, and systemd isn't here. Run 'npm i -g ${PACKAGE}' and start 'levix' yourself."
|
|
62
|
+
|
|
63
|
+
say "Node $(node -v) — good."
|
|
64
|
+
|
|
65
|
+
# --- 2. The package --------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
if [ "$VERSION" = "latest" ]; then
|
|
68
|
+
say "Installing ${PACKAGE}…"
|
|
69
|
+
else
|
|
70
|
+
say "Installing ${PACKAGE} ${VERSION}…"
|
|
71
|
+
fi
|
|
72
|
+
sudo_if_needed npm install -g "${PACKAGE}@${VERSION}"
|
|
73
|
+
|
|
74
|
+
# Where did it land? `/usr/bin/env levix` in the unit would depend on systemd's
|
|
75
|
+
# PATH, which does not include nvm's prefix and is not guaranteed to include
|
|
76
|
+
# npm's. Resolve it now and write the real path into the unit.
|
|
77
|
+
LEVIX_BIN="$(command -v levix || true)"
|
|
78
|
+
if [ -z "$LEVIX_BIN" ]; then
|
|
79
|
+
NPM_BIN="$(npm bin -g 2>/dev/null || npm prefix -g 2>/dev/null)/bin"
|
|
80
|
+
[ -x "${NPM_BIN}/levix" ] && LEVIX_BIN="${NPM_BIN}/levix"
|
|
81
|
+
fi
|
|
82
|
+
[ -n "$LEVIX_BIN" ] || die "levix installed but could not be found on PATH."
|
|
83
|
+
say "Installed at ${LEVIX_BIN}"
|
|
84
|
+
|
|
85
|
+
# --- 3. User and data directory -------------------------------------------
|
|
86
|
+
|
|
87
|
+
if ! id -u levix >/dev/null 2>&1; then
|
|
88
|
+
say "Creating the levix system user…"
|
|
89
|
+
sudo_if_needed useradd --system --home-dir "$DATA_DIR" --shell /usr/sbin/nologin levix
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
sudo_if_needed mkdir -p "$DATA_DIR"
|
|
93
|
+
sudo_if_needed chown -R levix:levix "$DATA_DIR"
|
|
94
|
+
# The database holds the WhatsApp session and the panel password hash. Nobody
|
|
95
|
+
# else on the machine has any business reading it.
|
|
96
|
+
sudo_if_needed chmod 750 "$DATA_DIR"
|
|
97
|
+
|
|
98
|
+
# --- 4. The service --------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
say "Installing the systemd service…"
|
|
101
|
+
|
|
102
|
+
UNIT_SOURCE=""
|
|
103
|
+
for candidate in \
|
|
104
|
+
"$(npm root -g 2>/dev/null)/${PACKAGE}/deploy/levix.service" \
|
|
105
|
+
"$(dirname "$0")/levix.service"; do
|
|
106
|
+
if [ -f "$candidate" ]; then UNIT_SOURCE="$candidate"; break; fi
|
|
107
|
+
done
|
|
108
|
+
[ -n "$UNIT_SOURCE" ] || die "Couldn't find the unit file to install."
|
|
109
|
+
|
|
110
|
+
# Written through a temp file in the same directory, then moved into place, so
|
|
111
|
+
# an interrupted run never leaves systemd a half-written unit. mktemp, not a
|
|
112
|
+
# predictable name in /tmp.
|
|
113
|
+
TMP_UNIT="$(mktemp)"
|
|
114
|
+
trap 'rm -f "$TMP_UNIT"' EXIT
|
|
115
|
+
|
|
116
|
+
sed "s|^ExecStart=.*|ExecStart=${LEVIX_BIN} --data ${DATA_DIR}|" "$UNIT_SOURCE" > "$TMP_UNIT"
|
|
117
|
+
grep -q "^ExecStart=${LEVIX_BIN}" "$TMP_UNIT" || die "Failed to set ExecStart in the unit file."
|
|
118
|
+
|
|
119
|
+
sudo_if_needed install -m 0644 -o root -g root "$TMP_UNIT" "$UNIT"
|
|
120
|
+
|
|
121
|
+
sudo_if_needed systemctl daemon-reload
|
|
122
|
+
sudo_if_needed systemctl enable "$SERVICE"
|
|
123
|
+
sudo_if_needed systemctl restart "$SERVICE"
|
|
124
|
+
|
|
125
|
+
# --- 5. Did it actually start? --------------------------------------------
|
|
126
|
+
|
|
127
|
+
started=0
|
|
128
|
+
for _ in $(seq 1 30); do
|
|
129
|
+
if systemctl is-active --quiet "$SERVICE"; then started=1; break; fi
|
|
130
|
+
sleep 1
|
|
131
|
+
done
|
|
132
|
+
|
|
133
|
+
if [ "$started" -ne 1 ]; then
|
|
134
|
+
printf '\n Levix was installed but the service did not start.\n\n'
|
|
135
|
+
printf ' What systemd says:\n\n'
|
|
136
|
+
sudo_if_needed systemctl status "$SERVICE" --no-pager --lines 20 || true
|
|
137
|
+
printf '\n Full log: journalctl -u %s -n 50\n\n' "$SERVICE"
|
|
138
|
+
exit 1
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
142
|
+
|
|
143
|
+
cat <<EOF
|
|
144
|
+
|
|
145
|
+
Levix is running.
|
|
146
|
+
|
|
147
|
+
Control panel http://${IP:-<this server>}:${PORT}/
|
|
148
|
+
Data ${DATA_DIR}
|
|
149
|
+
Logs journalctl -u ${SERVICE} -f
|
|
150
|
+
|
|
151
|
+
Open the panel and pick a password. Opening it from another machine also
|
|
152
|
+
asks for a setup code — this prints it:
|
|
153
|
+
|
|
154
|
+
journalctl -u ${SERVICE} -n 40 | grep -A3 'Setup code'
|
|
155
|
+
|
|
156
|
+
Then scan the QR from the Connection screen with WhatsApp.
|
|
157
|
+
|
|
158
|
+
EOF
|
|
159
|
+
|
|
160
|
+
# --- 6. A domain, if they want one now ------------------------------------
|
|
161
|
+
|
|
162
|
+
# Optional, and entirely skippable: an IP is a perfectly good way to run this,
|
|
163
|
+
# and `levix domain` works just as well next week. Only ask when there is
|
|
164
|
+
# somebody at the keyboard — piping this script into bash gives us no stdin.
|
|
165
|
+
if [ -t 0 ]; then
|
|
166
|
+
printf ' The panel has no HTTPS of its own. Connect a domain now? [y/N] '
|
|
167
|
+
read -r answer </dev/tty || answer=""
|
|
168
|
+
case "$answer" in
|
|
169
|
+
[yY]*)
|
|
170
|
+
# One implementation, in the CLI. This script does not have its own.
|
|
171
|
+
sudo_if_needed "$LEVIX_BIN" --data "$DATA_DIR" domain
|
|
172
|
+
;;
|
|
173
|
+
*)
|
|
174
|
+
printf '\n Fine — run this whenever you are ready:\n\n sudo levix --data %s domain bot.example.com\n\n' "$DATA_DIR"
|
|
175
|
+
;;
|
|
176
|
+
esac
|
|
177
|
+
else
|
|
178
|
+
printf ' To put it behind a domain with HTTPS:\n\n sudo levix --data %s domain bot.example.com\n\n' "$DATA_DIR"
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
printf ' If this server is on the public internet, do that (or put it behind\n'
|
|
182
|
+
printf ' your own reverse proxy) before sharing the panel address.\n\n'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# systemd unit for Levix.
|
|
2
|
+
#
|
|
3
|
+
# sudo cp deploy/levix.service /etc/systemd/system/
|
|
4
|
+
# sudo systemctl enable --now levix
|
|
5
|
+
# journalctl -u levix -f # the setup code is printed here on first run
|
|
6
|
+
#
|
|
7
|
+
# deploy/install.sh installs this and rewrites ExecStart with the real path of
|
|
8
|
+
# the `levix` command it just installed — `/usr/bin/env levix` only works when
|
|
9
|
+
# npm's global bin is on systemd's own PATH, which it is not under nvm and not
|
|
10
|
+
# on every distro. If you are installing this by hand, check that line.
|
|
11
|
+
|
|
12
|
+
[Unit]
|
|
13
|
+
Description=Levix WhatsApp bot
|
|
14
|
+
Documentation=https://github.com/Abdodiab2005/levix
|
|
15
|
+
After=network-online.target
|
|
16
|
+
Wants=network-online.target
|
|
17
|
+
|
|
18
|
+
# A misconfiguration — a port permanently taken, a corrupt install — must not
|
|
19
|
+
# turn into a restart loop that spins the CPU and fills the journal forever.
|
|
20
|
+
# Five failures in a minute and systemd stops trying, leaving the unit failed
|
|
21
|
+
# where `systemctl status` will show it.
|
|
22
|
+
StartLimitIntervalSec=60
|
|
23
|
+
StartLimitBurst=5
|
|
24
|
+
|
|
25
|
+
[Service]
|
|
26
|
+
Type=simple
|
|
27
|
+
User=levix
|
|
28
|
+
Group=levix
|
|
29
|
+
ExecStart=/usr/bin/env levix --data /var/lib/levix
|
|
30
|
+
Restart=always
|
|
31
|
+
# Long enough not to hammer, short enough to recover from a blip unattended.
|
|
32
|
+
RestartSec=5
|
|
33
|
+
KillSignal=SIGTERM
|
|
34
|
+
# Long enough for the database to checkpoint and the socket to close cleanly.
|
|
35
|
+
TimeoutStopSec=30
|
|
36
|
+
|
|
37
|
+
# Everything it writes is under one directory, which makes the rest of this
|
|
38
|
+
# easy. StateDirectory creates it and fixes its ownership on every start.
|
|
39
|
+
StateDirectory=levix
|
|
40
|
+
WorkingDirectory=/var/lib/levix
|
|
41
|
+
|
|
42
|
+
NoNewPrivileges=true
|
|
43
|
+
PrivateTmp=true
|
|
44
|
+
ProtectSystem=strict
|
|
45
|
+
ProtectHome=true
|
|
46
|
+
ReadWritePaths=/var/lib/levix
|
|
47
|
+
ProtectKernelTunables=true
|
|
48
|
+
ProtectControlGroups=true
|
|
49
|
+
RestrictSUIDSGID=true
|
|
50
|
+
# The bot needs outbound network and nothing else; it binds an ordinary port,
|
|
51
|
+
# so it never needs a capability.
|
|
52
|
+
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
53
|
+
CapabilityBoundingSet=
|
|
54
|
+
|
|
55
|
+
[Install]
|
|
56
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Reference Nginx configuration for the Levix download host.
|
|
2
|
+
#
|
|
3
|
+
# This file is documentation, not something Levix installs. Copy the parts you
|
|
4
|
+
# need into the server block that already serves levix.leviro.net.
|
|
5
|
+
#
|
|
6
|
+
# Two things live on that hostname:
|
|
7
|
+
#
|
|
8
|
+
# /install.sh, /install, /install/* static files, published by a release
|
|
9
|
+
# everything else the Next.js site, on 127.0.0.1:3000
|
|
10
|
+
#
|
|
11
|
+
# The installers are plain files on disk. Publishing a release replaces one of
|
|
12
|
+
# them with a rename; nothing here needs a reload, a restart, or an Nginx that
|
|
13
|
+
# knows anything about releases.
|
|
14
|
+
|
|
15
|
+
server {
|
|
16
|
+
listen 443 ssl;
|
|
17
|
+
listen [::]:443 ssl;
|
|
18
|
+
server_name levix.leviro.net;
|
|
19
|
+
|
|
20
|
+
# ssl_certificate / ssl_certificate_key — whatever certbot or your existing
|
|
21
|
+
# setup already wrote for this host.
|
|
22
|
+
|
|
23
|
+
charset utf-8;
|
|
24
|
+
|
|
25
|
+
# `location =` is an exact match and Nginx settles it before it looks at
|
|
26
|
+
# anything else, so this can never fall through to the proxy below.
|
|
27
|
+
location = /install.sh {
|
|
28
|
+
alias /var/www/levix-downloads/install.sh;
|
|
29
|
+
|
|
30
|
+
# Readable in a browser. Anyone about to pipe this into a shell should
|
|
31
|
+
# be able to click the URL and read it first — that is the whole point
|
|
32
|
+
# of publishing the source rather than a binary.
|
|
33
|
+
default_type text/plain;
|
|
34
|
+
add_header Content-Disposition "inline" always;
|
|
35
|
+
|
|
36
|
+
# A moving alias. Serving yesterday's stable installer from a cache
|
|
37
|
+
# after a release is the one failure this endpoint must not have.
|
|
38
|
+
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Convenience only. The command in the documentation stays /install.sh.
|
|
42
|
+
location = /install {
|
|
43
|
+
return 302 /install.sh;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# `^~` stops Nginx from considering any regex location for these, so a
|
|
47
|
+
# future regex rule for the site cannot start swallowing installer URLs.
|
|
48
|
+
location ^~ /install/ {
|
|
49
|
+
alias /var/www/levix-downloads/install/;
|
|
50
|
+
|
|
51
|
+
default_type text/plain;
|
|
52
|
+
add_header Content-Disposition "inline" always;
|
|
53
|
+
|
|
54
|
+
# These files are immutable: v2.0.1.sh is written once and never
|
|
55
|
+
# rewritten, so it can be cached for as long as anyone likes.
|
|
56
|
+
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
57
|
+
|
|
58
|
+
autoindex off;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Everything else is the website, which knows nothing about any of this.
|
|
62
|
+
location / {
|
|
63
|
+
proxy_pass http://127.0.0.1:3000;
|
|
64
|
+
proxy_http_version 1.1;
|
|
65
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
66
|
+
proxy_set_header Connection $connection_upgrade;
|
|
67
|
+
proxy_set_header Host $host;
|
|
68
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
69
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
70
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# For the Upgrade header above, if the site does not already define it:
|
|
75
|
+
#
|
|
76
|
+
# map $http_upgrade $connection_upgrade {
|
|
77
|
+
# default upgrade;
|
|
78
|
+
# '' close;
|
|
79
|
+
# }
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "levix-bot",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "A self-hosted personal WhatsApp bot with an AI agent, group moderation and a web control panel.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node bin/levix.js",
|
|
8
|
+
"dev": "nodemon bin/levix.js",
|
|
9
|
+
"test": "node tests/run.mjs",
|
|
10
|
+
"brand:assets": "node scripts/build-brand-assets.mjs",
|
|
11
|
+
"build:sea": "node scripts/build-sea.mjs",
|
|
12
|
+
"validate:tarball": "node scripts/validate-tarball.mjs",
|
|
13
|
+
"validate:sea": "node scripts/validate-sea.mjs",
|
|
14
|
+
"validate:docker": "bash scripts/validate-docker.sh"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"whatsapp",
|
|
18
|
+
"bot",
|
|
19
|
+
"baileys",
|
|
20
|
+
"gemini",
|
|
21
|
+
"ai-agent",
|
|
22
|
+
"sqlite",
|
|
23
|
+
"self-hosted"
|
|
24
|
+
],
|
|
25
|
+
"author": "Abdelrhman Diab (Leviro)",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@google/generative-ai": "^0.24.1",
|
|
30
|
+
"@whiskeysockets/baileys": "7.0.0-rc13",
|
|
31
|
+
"axios": "^1.9.0",
|
|
32
|
+
"ejs": "^3.1.10",
|
|
33
|
+
"express": "^5.1.0",
|
|
34
|
+
"express-session": "^1.18.1",
|
|
35
|
+
"ffmpeg-static": "^5.2.0",
|
|
36
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
37
|
+
"node-cache": "^5.1.2",
|
|
38
|
+
"node-cron": "^4.1.0",
|
|
39
|
+
"node-gtts": "^2.0.2",
|
|
40
|
+
"pino": "^9.7.0",
|
|
41
|
+
"pino-pretty": "^13.0.0",
|
|
42
|
+
"qrcode": "^1.5.4",
|
|
43
|
+
"qrcode-terminal": "^0.12.0",
|
|
44
|
+
"socket.io": "^4.8.1",
|
|
45
|
+
"uuid": "^11.1.0"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/Abdodiab2005/levix.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/Abdodiab2005/levix",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/Abdodiab2005/levix/issues"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=24"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"esbuild": "^0.28.2",
|
|
60
|
+
"postject": "^1.0.0-alpha.6",
|
|
61
|
+
"sharp": "^0.35.3"
|
|
62
|
+
},
|
|
63
|
+
"bin": {
|
|
64
|
+
"levix": "bin/levix.js"
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"bin/",
|
|
68
|
+
"src/",
|
|
69
|
+
"views/",
|
|
70
|
+
"public/",
|
|
71
|
+
"deploy/",
|
|
72
|
+
"app.cjs",
|
|
73
|
+
"scheduler.cjs",
|
|
74
|
+
"README.md",
|
|
75
|
+
"SETUP.md",
|
|
76
|
+
"LICENSE"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="levix-gradient" x1="15" y1="86" x2="87" y2="14" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#0045FF"/>
|
|
5
|
+
<stop offset=".55" stop-color="#0066F5"/>
|
|
6
|
+
<stop offset="1" stop-color="#00D4FF"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<path d="M75.5 55.2C77.8 50.9 79 46 79 41C79 22 63.5 7.5 44 7.5C24.2 7.5 8.5 22.1 8.5 41C8.5 53.5 15.8 64.6 27.2 70.4L23.3 84.5L39.6 76.7C44.3 78.1 49.2 78.4 54.4 76.8" stroke="url(#levix-gradient)" stroke-width="8.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
10
|
+
<circle cx="34.8" cy="42.7" r="4.1" fill="url(#levix-gradient)"/>
|
|
11
|
+
<circle cx="55.4" cy="42.7" r="4.1" fill="url(#levix-gradient)"/>
|
|
12
|
+
<path d="M39.5 51.8C42.7 55.8 47.7 55.8 50.9 51.8" stroke="url(#levix-gradient)" stroke-width="3.8" stroke-linecap="round"/>
|
|
13
|
+
<path d="M59.2 59.2L83.2 83.2M84.2 57.8L58.4 83.2" stroke="url(#levix-gradient)" stroke-width="8.2" stroke-linecap="round"/>
|
|
14
|
+
<path d="M83.2 16.5C84.3 20.7 86.3 22.8 90.5 23.9C86.3 25 84.3 27.1 83.2 31.3C82.1 27.1 80.1 25 75.9 23.9C80.1 22.8 82.1 20.7 83.2 16.5Z" fill="url(#levix-gradient)"/>
|
|
15
|
+
</svg>
|
|
16
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|