Skip to content

Commit fdd285d

Browse files
committed
refactor(bot): files, including app.ts, AdminCommands.ts, UserCommands.ts, BotMiddleware.ts, ReportCommand.ts, Catch.ts, and ChatInfo.ts, with changes to error handling, admin checks, and message replies.
1 parent 1733a9a commit fdd285d

12 files changed

Lines changed: 115 additions & 1123 deletions

File tree

package-lock.json

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

package.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"test": "mocha --config .mochar.json",
2020
"clean": "rm -rf dist",
2121
"main": "npm run build & node --env-file=.env dist/app.js",
22-
"start": "NODE_ENV=development node -r ts-node/register --env-file=.env src/app.ts",
22+
"start": "NODE_ENV=development node --trace-uncaught -r ts-node/register --env-file=.env src/app.ts",
2323
"start:deploy": "npm run build && NODE_ENV=production node dist/app.js"
2424
},
2525
"repository": {
@@ -46,12 +46,7 @@
4646
"pg": "^8.13.1"
4747
},
4848
"devDependencies": {
49-
"@types/chai": "^4.3.16",
50-
"@types/mocha": "^10.0.7",
5149
"@types/pg": "^8.11.10",
52-
"@types/supertest": "^6.0.2",
53-
"chai": "^5.1.1",
54-
"mocha": "^10.7.0",
5550
"ts-node": "^10.9.2",
5651
"typescript": "^5.5.4"
5752
}

src/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async function main() {
1313
const db = ServiceProvider.getInstance();
1414
await db.close();
1515
logger.info('Database closed.');
16-
} catch (error:any) {
16+
} catch (error: any) {
1717
logger.error('Error during shutdown:', error);
1818
} finally {
1919
process.exit(0);
@@ -25,7 +25,7 @@ async function main() {
2525

2626
process.on('uncaughtException', (error) => {
2727
console.error('Uncaught Exception:', error);
28-
process.exit(1);
28+
process.exit(1);
2929
});
3030
}
3131
main().catch((error) => console.error('Application error:', error));

src/bot/commands/admin/AdminCommands.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { Context, InlineKeyboard } from 'grammy';
1+
import { Context } from 'grammy';
22
import { BotReply } from '../../../utils/chat/BotReply';
33
import { Catch } from '../../../decorators/Catch';
44
import { AdminValidationService } from '../../service/admin/validation';
55
import { ApprovedService } from '../../service/admin/Approved';
66
import { BanService } from '../../service/admin/Ban';
77
import { WarnService } from '../../service/admin/Warn';
8-
import { parseDuration, tehranZone } from '../../../utils';
98
import { MuteService } from '../../service/admin/Mute';
109
import { AdminService } from '../../service/admin/Admin';
1110
import { BlackListService } from '../../service/admin/Blacklist';
1211
import { ChatInfo } from '../../../utils/chat/ChatInfo';
1312
import { GroupSettingsService } from '../../service/admin/Welcome';
13+
import { User } from 'grammy/types';
1414
export class AdminCommands {
1515
/** Approved Commands */
1616
@Catch({
@@ -119,8 +119,11 @@ export class AdminCommands {
119119
}
120120
static async warns(ctx: Context) {
121121
const reply = new BotReply(ctx);
122-
const user = ctx.message?.reply_to_message?.from!;
123-
122+
const replyMessage = ctx.message?.reply_to_message;
123+
let user = ctx.message?.reply_to_message?.from!;
124+
if (replyMessage?.forum_topic_created) {
125+
user = ctx.from!;
126+
}
124127
const { warnings } = await WarnService.getUserWarnById(ctx);
125128

126129
if (warnings >= 0) {

src/bot/commands/user/UserCommands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export class UserCommands {
6666
* or when someone replies to the bot with this command.
6767
* This is typically used for fun purposes,
6868
*/
69+
@Catch()
6970
static async codetime(ctx: Context) {
7071
const user = ctx.from;
7172
if (!user) return;

src/bot/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class CopBot {
4444
}
4545
} else {
4646
try {
47+
await this._bot.api.getUpdates({ offset: -1 });
4748
await this._bot.start({
4849
onStart: (botInfo) => {
4950
console.log(`Bot started in long-polling mode! Username: ${botInfo.username}`);
@@ -135,7 +136,8 @@ export class CopBot {
135136
@Catch()
136137
async initial(): Promise<void> {
137138
new GenerateCommand(this._bot).generate();
138-
await this.start();
139139
await this.message();
140+
await this.start();
141+
console.log('Bot Is Start');
140142
}
141143
}

src/bot/middleware/BotMiddleware.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ export class BotMiddleware {
88
const reply = new BotReply(ctx);
99
const chatInfo = new ChatInfo(ctx);
1010
const userIsAdmin = await chatInfo.userIsAdmin();
11+
const command = ctx.message?.text?.split('/')[1].split(' ')[0].toLowerCase().trim();
12+
if (command === 'warns') {
13+
if (isMiddleware) {
14+
return nxt!();
15+
} else {
16+
return true;
17+
}
18+
}
1119
if (!userIsAdmin) {
1220
await reply.textReply('Sorry, but you need to be an admin to use this command!');
1321
return false;
@@ -59,7 +67,14 @@ Sorry, but I don't have the necessary permissions to perform this action. Please
5967
*/
6068
static async adminCheckForRepliedUser(ctx: Context, nxt: NextFunction | null, isMiddleware: boolean = true) {
6169
const reply = new BotReply(ctx);
62-
const repliedUserId = ctx.message?.reply_to_message?.from?.id!;
70+
const replyMeseage = ctx.message?.reply_to_message!;
71+
let repliedUserId: number | null;
72+
if (replyMeseage && !replyMeseage?.forum_topic_created) {
73+
repliedUserId = replyMeseage!.from?.id!;
74+
} else {
75+
repliedUserId = null;
76+
}
77+
6378
const admins = await ctx.api.getChatAdministrators(ctx.chat!.id);
6479
const isAdmin = admins!.some((admin) => admin.user.id === repliedUserId);
6580
const command = ctx.message?.text?.split('/')[1].split(' ')[0].toLowerCase().trim();

src/bot/service/user/ReportCommand.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Context } from 'grammy';
22
import { BotReply } from '../../../utils/chat/BotReply';
3+
import { ChatInfo } from '../../../utils/chat/ChatInfo';
34

45
export class ReportCommand {
56
// Store pending reports with user IDs as keys
@@ -13,11 +14,16 @@ export class ReportCommand {
1314
*/
1415
static async report(ctx: Context) {
1516
const reply = new BotReply(ctx);
16-
17+
const chatInfo = new ChatInfo(ctx);
18+
const isAdmin = await chatInfo.isAdmin(ctx.message?.reply_to_message?.from!.id!);
19+
if (isAdmin) {
20+
await reply.textReply('This user is an admin and cannot be processed for this action.');
21+
return;
22+
}
1723
// Ensure the report command is a reply to another message
1824
const reportedMessage = ctx.message?.reply_to_message;
1925
if (!reportedMessage || !reportedMessage.from) {
20-
await ctx.reply('Please use the /report command by replying to the message you want to report.');
26+
await reply.textReply('Please use the /report command by replying to the message you want to report.');
2127
return;
2228
}
2329

src/decorators/Catch.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Context } from 'grammy';
1+
import { Context, GrammyError } from 'grammy';
22
import { ErrorResponse } from '../types/ResponseTypes';
33
import logger from '../utils/logger';
44
import { ServiceProvider } from '../service/database/ServiceProvider';
@@ -10,6 +10,10 @@ export function Catch(customResponse?: ErrorResponse) {
1010
try {
1111
return await originalMethod.apply(this, args);
1212
} catch (error: any) {
13+
if (error instanceof GrammyError && error.error_code === 400 && error.description === 'Bad Request: message to be replied not found') {
14+
console.warn(`Message not found to reply to. Skipping...`);
15+
return;
16+
}
1317
if (error.error_code === 403 && error.description.includes('bot was kicked')) {
1418
const chatId = error.payload?.chat_id;
1519
logger.warn(`[Warning] Bot was kicked from a group (chat_id: ${error.payload.chat_id}). Skipping.`);
@@ -25,10 +29,6 @@ export function Catch(customResponse?: ErrorResponse) {
2529
category: 'General',
2630
};
2731
logger.error(`[Category: ${errorResponse.category}] Error in ${target.constructor.name}.${propKey}(): ${error.message}`);
28-
// Send the error message to the user (if context is available)
29-
if (ctx && typeof ctx.reply === 'function') {
30-
await ctx.reply(errorResponse.message);
31-
}
3232
}
3333
};
3434

src/service/messages/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class MessagesService {
4646

4747
if (user.id !== this._ctx.me?.id) {
4848
// Notify the group about the member leaving
49-
const username = user.username ? `@${user.username}` : user.first_name;
49+
const username = user.username ? `${user.username}` : user.first_name;
5050
await this._ctx.reply(`${username} has left the chat.`);
5151
}
5252
}
@@ -71,7 +71,7 @@ export class MessagesService {
7171
group = await groupService.save(this._ctx);
7272
}
7373
const approvedUsers = group.approved_users.map(Number);
74-
const isAdmin = await this._chatInfo.isAdmin(this._ctx, userId);
74+
const isAdmin = await this._chatInfo.isAdmin(userId);
7575
// Allow approved users and admins to bypass blacklist checks
7676
if (approvedUsers.includes(+userId) || isAdmin) {
7777
return;

0 commit comments

Comments
 (0)