Skip to content

Commit 3dbf777

Browse files
committed
feat(database): add GroupRuleService, GroupService, and UserService models
- Added `GroupRuleService`: - Handles CRUD operations for group rules. - Implements methods to fetch, add, update, and delete group rules. - Ensures groups and users exist before adding or updating rules. - Added `UserService`: - Manages user-related operations like creation, retrieval, and updates. - Ensures users are persisted in the database with proper data handling. - Added `GroupService`: - Handles CRUD operations for groups. - Ensures group information is properly saved and updated, including members and permissions. - Supports synchronization with Telegram group data. - Integrated database layer via `DatabaseService`: - All services utilize consistent database operations for queries, inserts, updates, and deletions. - Improved modularity and reusability: - Services are designed to encapsulate specific responsibilities, ensuring clean architecture and easier future maintenance.
1 parent 1241805 commit 3dbf777

3 files changed

Lines changed: 248 additions & 0 deletions

File tree

src/database/models/Group.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { PoolClient } from 'pg';
2+
import { Group } from '../../types/database/TablesTypes';
3+
import { Context } from 'grammy';
4+
import { MembersService } from '../service/Members';
5+
import { DatabaseService } from '../service/Databas';
6+
export class GroupService {
7+
private _db: DatabaseService;
8+
constructor(private _client: PoolClient) {
9+
this._db = new DatabaseService(_client);
10+
}
11+
async create(group: Omit<Group, 'id'>) {
12+
return await this._db.insert<Group>('Group', {
13+
group_id: group.group_id,
14+
group_name: group.group_name,
15+
black_list: group.black_list || [],
16+
chat_permissions: JSON.stringify(group.chat_permissions),
17+
approved_users: group.approved_users || [],
18+
warnings: group.warnings || [],
19+
is_spam_time: group.is_spam_time || false,
20+
members: group.members || [],
21+
updated_at: group.updated_at || new Date(),
22+
joined_at: group.joined_at || new Date(),
23+
});
24+
}
25+
async update(group: Group) {
26+
const condition = { group_id: group.group_id };
27+
const data = {
28+
group_id: group.group_id,
29+
group_name: group.group_name,
30+
black_list: group.black_list,
31+
chat_permissions: group.chat_permissions,
32+
approved_users: group.approved_users,
33+
warnings: group.warnings,
34+
is_spam_time: group.is_spam_time,
35+
members: group.members,
36+
updated_at: group.updated_at || new Date(),
37+
};
38+
return await this._db.update<Group>('Group', data, condition);
39+
}
40+
async delete(groupId: number) {}
41+
async getByGroupId(groupId: number): Promise<Group | null> {
42+
const query = `SELECT * FROM "Group" WHERE group_id = $1;`;
43+
const result = await this._db.query<Group>(query, [groupId]);
44+
return result.rows[0] || null;
45+
}
46+
async save(ctx: Context): Promise<Group> {
47+
const [id, title] = [ctx.chat!.id!, ctx.chat!.title];
48+
const perrmission = (await ctx.api.getChat(id)).permissions;
49+
let group = await this.getByGroupId(id);
50+
if (!group) {
51+
const newGroupData: Omit<Group, 'id'> = {
52+
joined_at: new Date(),
53+
updated_at: new Date(),
54+
group_id: id,
55+
group_name: title || 'Unnamed Group',
56+
black_list: [],
57+
chat_permissions: perrmission || {},
58+
approved_users: [],
59+
warnings: 0,
60+
is_spam_time: false,
61+
members: [],
62+
};
63+
group = await this.create(newGroupData);
64+
}
65+
return group!;
66+
}
67+
private getMemebrsService() {
68+
return new MembersService(this._client);
69+
}
70+
async updateMembers(groupId: number, newMember: number | string): Promise<Group> {
71+
return await this.getMemebrsService().update(groupId, newMember);
72+
}
73+
}

src/database/models/GroupRule.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { PoolClient } from 'pg';
2+
import { GroupRule } from '../../types/database/TablesTypes';
3+
import { ServiceProvider } from '../../service/database/ServiceProvider';
4+
import { Context } from 'grammy';
5+
import { DatabaseService } from '../service/Databas';
6+
7+
export class GroupRuleService {
8+
private _db: DatabaseService;
9+
10+
constructor(private _client: PoolClient) {
11+
this._db = new DatabaseService(this._client);
12+
}
13+
private async createRule(groupRule: Omit<GroupRule, 'id'>): Promise<GroupRule> {
14+
return await this._db.insert<GroupRule>('GroupRule', {
15+
group_id: groupRule.group_id,
16+
rule_text: groupRule.rule_text,
17+
added_at: groupRule.added_at || new Date(),
18+
added_by: groupRule.added_by,
19+
});
20+
}
21+
private async update(ruleId: number, newRuleText: string[]): Promise<GroupRule> {
22+
const result = await this._db.update<GroupRule>('GroupRule', { rule_text: newRuleText, added_at: new Date() }, { id: ruleId });
23+
return result;
24+
}
25+
/**
26+
* Fetches all rules for a specific group, sorted by added date (most recent first).
27+
*/
28+
async getRulesByGroupId(groupId: number): Promise<GroupRule[]> {
29+
const services = ServiceProvider.getInstance();
30+
const groupService = await services.getGroupService();
31+
let group = await groupService.getByGroupId(groupId);
32+
const result = await this._db.query<GroupRule>('SELECT id, group_id, rule_text, added_at, added_by FROM "GroupRule" WHERE group_id = $1 ORDER BY added_at DESC;', [group?.id!]);
33+
return result.rows;
34+
}
35+
/**
36+
* Adds a new rule to a group.
37+
*/
38+
async addGroupRule(ctx: Context, ruleText: string): Promise<GroupRule> {
39+
const chat = ctx.chat!;
40+
const groupId = chat.id;
41+
const userId = ctx.from?.id!;
42+
43+
const services = ServiceProvider.getInstance();
44+
const [groupService, userService] = await Promise.all([services.getGroupService(), services.getUserService()]);
45+
// Ensure the group exists in the database
46+
let group = await groupService.getByGroupId(groupId);
47+
if (!group) {
48+
group = await groupService.save(ctx); // Create group if not found
49+
}
50+
// Ensure the user exists in the database
51+
let user = await userService.getByTelegramId(userId);
52+
if (!user) {
53+
const userData = { first_name: ctx.from?.first_name!, id: userId, username: ctx.from?.username! };
54+
user = await userService.save(userData); // Create user if not found
55+
}
56+
// Check if there's an existing rule for this group
57+
const groupRules = await this.getRulesByGroupId(groupId);
58+
if (groupRules.length > 0) {
59+
// Update the existing rule's text array by appending the new rule
60+
const existingRule = groupRules[0]; // Assuming only one entry per group
61+
const updatedRuleText = [...existingRule.rule_text, ruleText];
62+
63+
return this.update(existingRule.id, updatedRuleText);
64+
} else {
65+
// Create a new rule entry if no rules exist
66+
return this.createRule({
67+
group_id: group.id,
68+
rule_text: [ruleText],
69+
added_at: new Date(),
70+
added_by: user.id,
71+
});
72+
}
73+
// Create the new group rule
74+
}
75+
/**
76+
* Deletes the most recently added rule for a group.
77+
*/
78+
async deleteLastGroupRule(ctx: Context): Promise<GroupRule | null> {
79+
const chat = ctx.chat!;
80+
const groupId = chat.id;
81+
const groupRules = await this.getRulesByGroupId(groupId);
82+
if (groupRules.length === 0) {
83+
// No rules to delete
84+
return null;
85+
}
86+
const latestRule = groupRules[0];
87+
const updatedRuleText = latestRule.rule_text.slice(0, -1);
88+
if (updatedRuleText.length === 0) {
89+
const result = await this._db.delete<GroupRule>('GroupRule', { id: latestRule.id });
90+
return result[0] || null;
91+
} else {
92+
// Update the rule_text array with the last element removed
93+
const result = await this._db.update<GroupRule>('GroupRule', { rule_text: updatedRuleText, added_at: new Date() }, { id: latestRule.id });
94+
return result;
95+
}
96+
}
97+
98+
/**
99+
* Deletes all rules for a specific group.
100+
*/
101+
async clearAllRulesForGroup(ctx: Context): Promise<void> {
102+
const chat = ctx.chat!;
103+
const groupId = chat.id;
104+
105+
const groupRules = await this.getRulesByGroupId(groupId);
106+
console.log('groupRules', groupRules);
107+
108+
if (groupRules.length === 0) {
109+
console.log(`No rules found for group ID: ${groupId}.`);
110+
return;
111+
}
112+
await this._db.delete<GroupRule>('GroupRule', { group_id: groupRules[0].group_id });
113+
}
114+
}

src/database/models/User.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { PoolClient } from 'pg';
2+
import { User } from '../../types/database/TablesTypes';
3+
import { DatabaseService } from '../service/Databas';
4+
export class UserService {
5+
private _db: DatabaseService;
6+
constructor(private _client: PoolClient) {
7+
this._db = new DatabaseService(_client);
8+
}
9+
async create(user: Omit<User, 'id'>): Promise<User> {
10+
return await this._db.insert<User>('User', {
11+
telegram_id: user.telegram_id,
12+
username: user.username,
13+
first_name: user.first_name,
14+
created_at: user.created_at || new Date(),
15+
updated_at: user.updated_at || new Date(),
16+
role: user.role,
17+
warnings: user.warnings,
18+
approved_groups: user.approved_groups,
19+
});
20+
}
21+
async update(user: User): Promise<User | null> {
22+
const condition = { id: user.id };
23+
const data = {
24+
telegram_id: user.telegram_id,
25+
username: user.username,
26+
first_name: user.first_name,
27+
role: user.role,
28+
warnings: user.warnings,
29+
approved_groups: user.approved_groups,
30+
updated_at: new Date(),
31+
};
32+
return await this._db.update<User>('User', data, condition);
33+
}
34+
async delete(telegram_id: number): Promise<boolean> {
35+
const deletedUser = await this._db.delete<User>('User', { telegram_id }, ['id']);
36+
return deletedUser.length > 0;
37+
}
38+
async getByTelegramId(telegram_id: number): Promise<User | null> {
39+
const query = `SELECT * FROM "User" WHERE telegram_id = $1;`;
40+
const result = await this._db.query<User>(query, [telegram_id]);
41+
return result.rows.length > 0 ? result.rows[0] : null;
42+
}
43+
async save(user_data: { id: number; username: string; first_name: string }): Promise<User> {
44+
const { id, username, first_name } = user_data;
45+
let user = await this.getByTelegramId(id);
46+
if (!user) {
47+
const newUserData: Omit<User, 'id'> = {
48+
created_at: new Date(),
49+
updated_at: new Date(),
50+
telegram_id: id,
51+
role: 'user',
52+
username: username || null,
53+
first_name: first_name,
54+
warnings: 0,
55+
approved_groups: [],
56+
};
57+
user = await this.create(newUserData);
58+
}
59+
return user!;
60+
}
61+
}

0 commit comments

Comments
 (0)