Skip to content

Commit 954d2c3

Browse files
committed
feat: Implement GroupService, UserService, and TablesService for database management
- Added `GroupService` for managing groups, including methods for creating, updating, deleting, retrieving by group ID, and managing rules and members. - Implemented `UserService` for managing users, including methods for creating, retrieving by Telegram ID, and saving user data. - Introduced `TablesService` to handle the initialization and seeding of database tables. - Added `save` method in both `GroupService` and `UserService` to automatically save group or user data if it doesn't already exist. - Created functionality in `GroupService` for adding and deleting rules, clearing all rules, and updating members. - Implemented error handling for the `initialTables` and `seedTables` methods in `TablesService` using custom decorators. - Added logic to prevent seeding if the `User` table already contains data in the `seedTables` method. - Ensured that `GroupService` and `UserService` both interact with the database using the `PoolClient` for connection pooling. This update enhances the database structure and service layer for managing groups and users, ensuring the proper initialization and seeding of tables, as well as providing methods for group and user management.
1 parent a7bc62d commit 954d2c3

3 files changed

Lines changed: 206 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { PoolClient } from 'pg';
2+
import { Group, User } from '../../../types/database/TablesTypes';
3+
import { Context } from 'grammy';
4+
export class GroupService {
5+
constructor(private _client: PoolClient) {}
6+
async create(group: Omit<Group, 'id'>) {
7+
const query = `
8+
INSERT INTO "Group" (
9+
group_id, group_name, rules, black_list,
10+
chat_permissions, updated_at, joined_at, approved_users,
11+
warnings, is_spam_time,members
12+
)
13+
VALUES (
14+
$1, $2, $3, $4, $5, NOW(), NOW(), $6, $7, $8, $9
15+
) RETURNING id,group_id, group_name, rules, black_list,chat_permissions, updated_at, joined_at, approved_users,warnings, is_spam_time,members;
16+
`;
17+
const values = [
18+
group.group_id,
19+
group.group_name,
20+
group.rules || [],
21+
group.black_list || [],
22+
JSON.stringify(group.chat_permissions),
23+
group.approved_users || [],
24+
group.warnings || [],
25+
group.is_spam_time || false,
26+
group.members || [],
27+
];
28+
const result = await this._client.query(query, values);
29+
return result.rows[0];
30+
}
31+
async update(group: Group) {
32+
const query = `
33+
UPDATE "Group"
34+
SET
35+
group_name = $1,
36+
rules = $2,
37+
black_list = $3,
38+
chat_permissions = $4,
39+
updated_at = NOW(),
40+
approved_users = $5,
41+
warnings = $6,
42+
is_spam_time = $7,
43+
members = $8
44+
WHERE group_id = $9
45+
RETURNING id, group_id, group_name, rules, black_list, chat_permissions, updated_at, approved_users, warnings, is_spam_time, members;
46+
`;
47+
48+
const values = [group.group_name, group.rules, group.black_list, JSON.stringify(group.chat_permissions), group.approved_users, group.warnings, group.is_spam_time, group.members, group.group_id];
49+
50+
const result = await this._client.query(query, values);
51+
return result.rows[0];
52+
}
53+
54+
async delete() {}
55+
async getByGroupId(groupId: number): Promise<Group | null> {
56+
const query = `SELECT * FROM "Group" WHERE group_id = $1;`;
57+
const result = await this._client.query(query, [groupId]);
58+
return result.rows[0] || null;
59+
}
60+
async save(ctx: Context): Promise<Group> {
61+
const [id, title] = [ctx.chat!.id!, ctx.chat!.title];
62+
let group = await this.getByGroupId(id);
63+
if (!group) {
64+
const newGroupData: Omit<Group, 'id'> = {
65+
group_id: id,
66+
group_name: title || 'Unnamed Group',
67+
rules: [],
68+
black_list: [],
69+
chat_permissions: {},
70+
approved_users: [],
71+
warnings: 0,
72+
is_spam_time: false,
73+
members: [],
74+
};
75+
group = await this.create(newGroupData);
76+
}
77+
return group!;
78+
}
79+
async addRule(groupId: number, newRule: string): Promise<void> {
80+
const group = await this.getByGroupId(groupId);
81+
if (!group) {
82+
throw new Error(`Group with ID ${groupId} not found.`);
83+
}
84+
85+
const updatedRules = [...group.rules, newRule]; // Add the new rule
86+
await this.update({ ...group, rules: updatedRules });
87+
}
88+
async getRules(groupId: number): Promise<string[]> {
89+
const query = `SELECT rules FROM "Group" WHERE group_id = $1;`;
90+
const result = await this._client.query(query, [groupId]);
91+
return result.rows[0]?.rules || [];
92+
}
93+
async updateMembers(groupId: number, newMember: number | string) {
94+
const group = await this.getByGroupId(groupId);
95+
if (!group) {
96+
throw new Error(`Group with ID ${groupId} not found`);
97+
}
98+
const members = group.members.map(Number);
99+
if (!members.includes(+newMember)) {
100+
members.push(+newMember);
101+
}
102+
const updatedGroup = await this.update({
103+
...group,
104+
members,
105+
});
106+
107+
return updatedGroup;
108+
}
109+
async deleteLastRule(groupId: number): Promise<string[] | null> {
110+
const group = await this.getByGroupId(groupId);
111+
if (!group || group.rules.length === 0) {
112+
return null; // No rules to delete
113+
}
114+
115+
const updatedRules = group.rules.slice(0, -1); // Remove the last rule
116+
await this.update({ ...group, rules: updatedRules });
117+
return updatedRules;
118+
}
119+
async clearRules(groupId: number): Promise<void> {
120+
const group = await this.getByGroupId(groupId);
121+
if (!group) {
122+
throw new Error(`Group with ID ${groupId} not found.`);
123+
}
124+
125+
await this.update({ ...group, rules: [] }); // Clear all rules
126+
}
127+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Catch } from '../../../decorators/ErrorHandlingDecorator';
2+
import { ConnectionPool } from '../../ConnectionPool';
3+
import * as path from 'path';
4+
import * as fs from 'fs/promises';
5+
export class TablesService {
6+
constructor(private _connectionPool: ConnectionPool) {}
7+
@Catch({
8+
category: 'Database',
9+
message: 'Failed to set up initial tables.',
10+
statusCode: 500,
11+
})
12+
async initialTables() {
13+
const client = await this._connectionPool.getClient();
14+
const sqlFilePath = path.join(__dirname, '..', '..', './sql/Tables.sql');
15+
const sql = await fs.readFile(sqlFilePath, 'utf-8');
16+
await client.query(sql);
17+
console.log('Initial tables have been set up successfully.');
18+
}
19+
@Catch({
20+
category: 'Database',
21+
message: 'Failed to seed tables.',
22+
statusCode: 500,
23+
})
24+
async seedTables() {
25+
const client = await this._connectionPool.getClient();
26+
const result = await client.query(`SELECT COUNT(*) FROM "User";`);
27+
const userCount = parseInt(result.rows[0].count, 10);
28+
if (userCount > 0) {
29+
console.log('The tables have already been seeded. Skipping seeding process.');
30+
return; // Skip seeding if there's already data in the User table
31+
}
32+
const sqlFilePath = path.join(__dirname, '..', '..', './sql/seed/SeedDataTables.sql');
33+
const sql = await fs.readFile(sqlFilePath, 'utf-8');
34+
await client.query(sql);
35+
console.log('All tables have been seeded successfully.');
36+
}
37+
}

src/database/service/user/User.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { PoolClient } from 'pg';
2+
import { User } from '../../../types/database/TablesTypes';
3+
import { Context } from 'grammy';
4+
export class UserService {
5+
constructor(private _client: PoolClient) {}
6+
async create(user: Omit<User, 'id'>) {
7+
const query = `
8+
INSERT INTO "User"(
9+
telegram_id, username, first_name, role,
10+
warnings, approved_groups
11+
)
12+
VALUES (
13+
$1, $2, $3, $4, $5, $6
14+
) RETURNING id,telegram_id, username, first_name, role,warnings, approved_groups;`;
15+
const values = [user.telegram_id, user.username, user.first_name, user.role, user.warnings, user.approved_groups];
16+
const result = await this._client.query(query, values);
17+
return result.rows[0];
18+
}
19+
async update() {}
20+
async delete() {}
21+
async getByTelegramId(telegram_id: number): Promise<User | null> {
22+
const query = `SELECT * FROM "User" WHERE telegram_id = $1;`;
23+
const result = await this._client.query(query, [telegram_id]);
24+
return result.rows[0] || null;
25+
}
26+
async save(ctx: Context): Promise<User> {
27+
const [id, username, first_name] = [ctx.from?.id!, ctx.from?.username, ctx.from?.first_name!];
28+
let user = await this.getByTelegramId(id);
29+
if (!user) {
30+
const newUserData: Omit<User, 'id'> = {
31+
telegram_id: id,
32+
role: 'user',
33+
username: username || null,
34+
first_name: first_name,
35+
warnings: 0,
36+
approved_groups: [],
37+
};
38+
user = await this.create(newUserData);
39+
}
40+
return user!;
41+
}
42+
}

0 commit comments

Comments
 (0)