Skip to content

Commit 2927051

Browse files
committed
feat: implement user blocking and unblocking commands, enhance user management, and add new database models
1 parent 16736d6 commit 2927051

16 files changed

Lines changed: 465 additions & 32 deletions

File tree

.env.example

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ AUTO_RELOAD=false
2222
# this should be a valid phone number in the format
2323
SUPER_ADMIN=09123456789
2424

25-
# the database provider (e.g., postgresql, mysql, sqlite)
26-
DATABASE_PROVIDER=mysql
27-
2825
# the database connection URL
2926
DATABASE_URL=mysql://root@localhost:3306/project_canis
3027

package-lock.json

Lines changed: 93 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
"npmlog": "^7.0.1",
3434
"qrcode-terminal": "^0.12.0",
3535
"systeminformation": "^5.27.7",
36-
"whatsapp-web.js": "^1.31.0"
36+
"whatsapp-web.js": "^1.31.0",
37+
"youtubei.js": "^15.0.0",
38+
"ytdl-core": "^4.11.5"
3739
},
3840
"devDependencies": {
3941
"@types/dotenv": "^6.1.1",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
-- CreateTable
2+
CREATE TABLE `User` (
3+
`id` INTEGER NOT NULL AUTO_INCREMENT,
4+
`lid` VARCHAR(191) NOT NULL,
5+
`name` VARCHAR(191) NOT NULL,
6+
`number` VARCHAR(191) NOT NULL,
7+
`countryCode` VARCHAR(191) NOT NULL,
8+
`type` VARCHAR(191) NOT NULL,
9+
`mode` VARCHAR(191) NOT NULL,
10+
`about` VARCHAR(191) NULL,
11+
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
12+
`updatedAt` DATETIME(3) NOT NULL,
13+
14+
UNIQUE INDEX `User_lid_key`(`lid`),
15+
PRIMARY KEY (`id`)
16+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
17+
18+
-- CreateTable
19+
CREATE TABLE `Block` (
20+
`id` INTEGER NOT NULL AUTO_INCREMENT,
21+
`lid` VARCHAR(191) NOT NULL,
22+
`mode` VARCHAR(191) NOT NULL,
23+
`reason` VARCHAR(191) NULL,
24+
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
25+
`updatedAt` DATETIME(3) NOT NULL,
26+
27+
UNIQUE INDEX `Block_lid_key`(`lid`),
28+
PRIMARY KEY (`id`)
29+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
30+
31+
-- CreateTable
32+
CREATE TABLE `Group` (
33+
`id` INTEGER NOT NULL AUTO_INCREMENT,
34+
`gid` VARCHAR(191) NOT NULL,
35+
`name` VARCHAR(191) NOT NULL,
36+
`description` VARCHAR(191) NULL,
37+
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
38+
`updatedAt` DATETIME(3) NOT NULL,
39+
40+
UNIQUE INDEX `Group_gid_key`(`gid`),
41+
PRIMARY KEY (`id`)
42+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (e.g., Git)
3+
provider = "mysql"

prisma/schema.prisma

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,37 @@ generator client {
1010
}
1111

1212
datasource db {
13-
provider = env("DATABASE_PROVIDER")
13+
provider = "mysql"
1414
url = env("DATABASE_URL")
1515
}
1616

1717
model User {
18-
id Int @id @default(autoincrement())
19-
name String
20-
number String @unique
21-
}
18+
id Int @id @default(autoincrement())
19+
lid String @unique
20+
name String
21+
number String
22+
countryCode String
23+
type String
24+
mode String
25+
about String?
26+
createdAt DateTime @default(now())
27+
updatedAt DateTime @updatedAt
28+
}
29+
30+
model Block {
31+
id Int @id @default(autoincrement())
32+
lid String @unique
33+
mode String
34+
reason String?
35+
createdAt DateTime @default(now())
36+
updatedAt DateTime @updatedAt
37+
}
38+
39+
model Group {
40+
id Int @id @default(autoincrement())
41+
gid String @unique
42+
name String
43+
description String?
44+
createdAt DateTime @default(now())
45+
updatedAt DateTime @updatedAt
46+
}

src/commands/block.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Message } from "whatsapp-web.js";
2+
import log from "../components/utils/log";
3+
import { exec } from "child_process";
4+
import util from "util";
5+
import { prisma } from "../components/prisma";
6+
7+
export const command = "block";
8+
export const role = "admin";
9+
10+
export default async function block(msg: Message) {
11+
if (msg.mentionedIds.length === 0) {
12+
await msg.reply("Please mention a user to block.");
13+
return;
14+
}
15+
16+
for (const userId of msg.mentionedIds) {
17+
const lid = userId.split("@")[0];
18+
19+
await prisma.block.upsert({
20+
where: { lid },
21+
update: {},
22+
create: { lid, mode: msg.author ? "group" : "private" },
23+
});
24+
}
25+
26+
await msg.react("✅");
27+
}

src/commands/play.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Message, MessageMedia } from "whatsapp-web.js";
2+
import fs from "fs";
3+
import path from "path";
4+
import innertube from "../components/innertube";
5+
import ytdl from "ytdl-core";
6+
import { client } from "../components/client";
7+
8+
export const command = "play";
9+
export const role = "admin";
10+
11+
// Type guard
12+
function isVideoNode(node: any): node is {
13+
type: string;
14+
video_id: string;
15+
title: { toString(): string };
16+
length_text?: { toString(): string };
17+
} {
18+
return node?.type === "Video" && typeof node.video_id === "string";
19+
}
20+
21+
export default async function play(msg: Message) {
22+
const query = msg.body.replace(/^play\b\s*/i, "").trim();
23+
if (!query) {
24+
await msg.reply("Please provide a search query.");
25+
return;
26+
}
27+
28+
const yt = await innertube();
29+
const search = await yt.search(query, { type: "video" });
30+
31+
const video = search.results.find((node) => isVideoNode(node));
32+
if (!video) {
33+
await msg.reply("Unable to find resources for the given query.");
34+
return;
35+
}
36+
37+
const videoId = video.video_id;
38+
const videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
39+
const title = video.title.toString();
40+
const duration = video.length_text?.toString() || "unknown";
41+
42+
await msg.reply(`🎵 Downloading audio for *${title}* (${duration})`);
43+
44+
const filePath = path.resolve(__dirname, `../../.temp/audio-${videoId}.webm`);
45+
const audioStream = ytdl(videoUrl, {
46+
filter: "audioonly",
47+
quality: "highestaudio",
48+
});
49+
const writeStream = fs.createWriteStream(filePath);
50+
audioStream.pipe(writeStream);
51+
52+
await new Promise<void>((resolve, reject) => {
53+
writeStream.on("finish", resolve);
54+
writeStream.on("error", reject);
55+
});
56+
57+
const audioBuffer = fs.readFileSync(filePath);
58+
const media = new MessageMedia(
59+
"audio/webm",
60+
audioBuffer.toString("base64"),
61+
`audio-${videoId}.webm`
62+
);
63+
await client.sendMessage(msg.from, media, {
64+
caption: `🎶 *${title}*`,
65+
});
66+
67+
fs.unlinkSync(filePath);
68+
}

src/commands/unblock.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Message } from "whatsapp-web.js";
2+
import log from "../components/utils/log";
3+
import { exec } from "child_process";
4+
import util from "util";
5+
import { prisma } from "../components/prisma";
6+
7+
export const command = "unblock";
8+
export const role = "admin";
9+
10+
export default async function unblock(msg: Message) {
11+
if (msg.mentionedIds.length === 0) {
12+
await msg.reply("Please mention a user to ublock.");
13+
return;
14+
}
15+
16+
for (const userId of msg.mentionedIds) {
17+
const lid = userId.split("@")[0];
18+
19+
await prisma.block.delete({
20+
where: { lid },
21+
});
22+
}
23+
24+
await msg.react("✅");
25+
}

0 commit comments

Comments
 (0)