signalblast 0.0.1.dev0__py3-none-any.whl

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.
File without changes
signalblast/admin.py ADDED
@@ -0,0 +1,84 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ import bcrypt
5
+
6
+ from signalblast.utils import get_code_data_path
7
+
8
+
9
+ class Admin:
10
+ save_path = get_code_data_path() / "admin.txt"
11
+
12
+ def __init__(self) -> None:
13
+ self.admin_id: str = None
14
+ self._hashed_password: str = None
15
+
16
+ @classmethod
17
+ async def create(cls, admin_password: Optional[str]) -> None:
18
+ self = Admin()
19
+ self.admin_id = None
20
+ await self.set_hashed_password(admin_password)
21
+ return self
22
+
23
+ def get_hashed_password(self):
24
+ return self._hashed_password
25
+
26
+ async def set_hashed_password(self, password: str):
27
+ if password is None:
28
+ self._hashed_password = "".encode()
29
+ else:
30
+ self._hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
31
+ await self.save_to_file()
32
+
33
+ async def add(self, id: str, admin_password: Optional[str]) -> bool:
34
+ if admin_password is None:
35
+ return False
36
+
37
+ if bcrypt.checkpw(admin_password.encode(), self.get_hashed_password()):
38
+ self.admin_id = id
39
+ await self.save_to_file()
40
+ return True
41
+ else:
42
+ return False
43
+
44
+ async def remove(self, admin_password: Optional[str]) -> bool:
45
+ if admin_password is None:
46
+ return False
47
+
48
+ if bcrypt.checkpw(admin_password.encode(), self.get_hashed_password()):
49
+ self.admin_id = None
50
+ await self.save_to_file()
51
+ return True
52
+ else:
53
+ return False
54
+
55
+ async def save_to_file(self) -> None:
56
+ with open(self.save_path, "w") as f:
57
+ if self.admin_id is None:
58
+ f.write("\n")
59
+ else:
60
+ f.write(self.admin_id + "\n")
61
+ f.write(self.get_hashed_password().decode())
62
+
63
+ @staticmethod
64
+ async def _load_from_file() -> "Admin":
65
+ admin = Admin()
66
+ with open(Admin.save_path, "r") as f:
67
+ admin.admin_id = f.readline().rstrip()
68
+ admin._hashed_password = f.readline().encode()
69
+
70
+ if admin.admin_id == "":
71
+ admin.admin_id = None
72
+
73
+ return admin
74
+
75
+ @staticmethod
76
+ async def load_from_file(admin_password: Optional[str]) -> "Admin":
77
+ if not os.path.exists(Admin.save_path):
78
+ return await Admin.create(admin_password)
79
+
80
+ admin = await Admin._load_from_file()
81
+ # Overwrite the password in the file, if no password was given assume we want to keep the one from the file
82
+ if admin_password is not None:
83
+ await admin.set_hashed_password(admin_password)
84
+ return admin
@@ -0,0 +1,537 @@
1
+ import functools
2
+ from asyncio import gather
3
+ from re import Pattern
4
+
5
+ from signalbot import Command
6
+ from signalbot import Context as ChatContext
7
+
8
+ from signalblast.bot_commands import AdminCommandStrings, CommandRegex, PublicCommandStrings
9
+ from signalblast.broadcastbot import BroadcasBot
10
+
11
+
12
+ def triggered(pattern: Pattern):
13
+ def decorator_triggered(func):
14
+ @functools.wraps(func)
15
+ async def wrapper_triggered(*args, **kwargs):
16
+ c: ChatContext = args[1]
17
+ text = c.message.text
18
+ if not isinstance(text, str):
19
+ return
20
+
21
+ if pattern.match(text) is None:
22
+ return
23
+
24
+ return await func(*args, **kwargs)
25
+
26
+ return wrapper_triggered
27
+
28
+ return decorator_triggered
29
+
30
+
31
+ class Subscribe(Command):
32
+ def __init__(self, bot: BroadcasBot) -> None:
33
+ super().__init__()
34
+ self.broadcastbot = bot
35
+
36
+ async def subscribe(self, ctx: ChatContext, verbose: bool = False) -> None:
37
+ try:
38
+ subscriber_uuid = ctx.message.source_uuid
39
+ if subscriber_uuid in self.broadcastbot.subscribers:
40
+ if verbose:
41
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Already subscribed!")
42
+ self.broadcastbot.logger.info("Already subscribed!")
43
+ return
44
+
45
+ if subscriber_uuid in self.broadcastbot.banned_users:
46
+ if verbose:
47
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "This number is not allowed to subscribe")
48
+ self.broadcastbot.logger.info(f"{subscriber_uuid} was not allowed to subscribe")
49
+ return
50
+
51
+ await self.broadcastbot.subscribers.add(subscriber_uuid, ctx.message.source_number)
52
+ if verbose:
53
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Subscription successful!")
54
+ # if self.broadcastbot.expiration_time is not None:
55
+ # await ctx.bot.set_expiration(subscriber_uuid, self.broadcastbot.expiration_time)
56
+ self.broadcastbot.logger.info(f"{subscriber_uuid} subscribed")
57
+ except Exception as e:
58
+ self.broadcastbot.logger.error(e, exc_info=True)
59
+ try:
60
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Could not subscribe!")
61
+ except Exception as e:
62
+ self.broadcastbot.logger.error(e, exc_info=True)
63
+
64
+ @triggered(CommandRegex.subscribe)
65
+ async def handle(self, ctx: ChatContext) -> None:
66
+ await Subscribe.subscribe(self, ctx, verbose=True)
67
+
68
+
69
+ class Unsubscribe(Command):
70
+ def __init__(self, bot: BroadcasBot) -> None:
71
+ super().__init__()
72
+ self.broadcastbot = bot
73
+
74
+ @triggered(CommandRegex.unsubscribe)
75
+ async def handle(self, ctx: ChatContext) -> None:
76
+ try:
77
+ subscriber_uuid = ctx.message.source_uuid
78
+
79
+ if subscriber_uuid not in self.broadcastbot.subscribers:
80
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Not subscribed!")
81
+ self.broadcastbot.logger.info(f"{subscriber_uuid} tried to unsubscribe but they are not subscribed")
82
+ return
83
+
84
+ await self.broadcastbot.subscribers.remove(subscriber_uuid)
85
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Successfully unsubscribed!")
86
+ self.broadcastbot.logger.info(f"{subscriber_uuid} unsubscribed")
87
+ except Exception as e:
88
+ self.broadcastbot.logger.error(e, exc_info=True)
89
+ try:
90
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Could not unsubscribe!")
91
+ except Exception as e:
92
+ self.broadcastbot.logger.error(e, exc_info=True)
93
+ # finally:
94
+ # raise StopPropagation
95
+
96
+
97
+ class Broadcast(Command):
98
+ def __init__(self, bot: BroadcasBot) -> None:
99
+ super().__init__()
100
+ self.broadcastbot = bot
101
+
102
+ def is_valid_command(self, message: str, invalid_command: Pattern) -> bool:
103
+ regex: Pattern
104
+ for regex in CommandRegex:
105
+ if regex != invalid_command and regex.search(message) is not None:
106
+ return True
107
+ return False
108
+
109
+ @staticmethod
110
+ async def broadcast(bot: BroadcasBot, ctx: ChatContext) -> None:
111
+ num_broadcasts = 0
112
+ num_subscribers = -1
113
+
114
+ try:
115
+ subscriber_uuid = ctx.message.source_uuid
116
+ if subscriber_uuid in bot.banned_users:
117
+ await bot.send(subscriber_uuid, "This number is not allowed to broadcast messages")
118
+ bot.logger.info(f"{subscriber_uuid} tried to broadcast but they are banned")
119
+ return
120
+
121
+ if subscriber_uuid not in bot.subscribers:
122
+ await bot.send(subscriber_uuid, bot.must_subscribe_message)
123
+ bot.logger.info(f"{subscriber_uuid} tried to broadcast but they are not subscribed")
124
+ return
125
+
126
+ num_subscribers = len(bot.subscribers)
127
+
128
+ # await ctx.message.mark_read()
129
+ message = bot.message_handler.remove_command_from_message(ctx.message.text, PublicCommandStrings.broadcast)
130
+ attachments = bot.message_handler.empty_list_to_none(ctx.message.base64_attachments)
131
+ # link_previews = bot.message_handler.empty_list_to_none(ctx.message.data_message.previews)
132
+
133
+ if message is None and attachments is None:
134
+ return
135
+
136
+ if message is None:
137
+ message = ""
138
+
139
+ # Broadcast message to all subscribers.
140
+ send_tasks = [None] * len(bot.subscribers)
141
+
142
+ for i, subscriber in enumerate(bot.subscribers):
143
+ send_tasks[i] = bot.send(subscriber, message, base64_attachments=attachments)
144
+ # link_previews=link_previews)
145
+
146
+ send_task_results = await gather(*send_tasks)
147
+
148
+ for send_task_result in send_task_results:
149
+ if isinstance(send_task_result, str):
150
+ num_broadcasts += 1
151
+ bot.logger.info(f"Message successfully sent to {subscriber}")
152
+ else:
153
+ bot.logger.warning(f"Could not send message to {subscriber}")
154
+ await bot.subscribers.remove(ctx.message.source_uuid)
155
+
156
+ attachments_filenames = bot.message_handler.empty_list_to_none(ctx.message.attachments_filenames)
157
+ bot.message_handler.delete_attachments(attachments_filenames, link_previews=None)
158
+
159
+ await bot.reply_with_warn_on_failure(ctx, f"Message sent to {num_broadcasts - 1} people")
160
+ except Exception as e:
161
+ bot.logger.error(e, exc_info=True)
162
+ try:
163
+ error_str = "Something went wrong when sending the message"
164
+ if num_broadcasts == num_subscribers:
165
+ error_str += ", but it was sent to everybody"
166
+ error_str += ", please contact the admin if the problem persists"
167
+ await bot.reply_with_warn_on_failure(ctx, error_str)
168
+ except Exception as e:
169
+ bot.logger.error(e, exc_info=True)
170
+ # finally:
171
+ # raise StopPropagation
172
+
173
+ async def handle(self, ctx: ChatContext) -> None:
174
+ message = ctx.message.text
175
+ subscriber_uuid = ctx.message.source_uuid
176
+ if message is None:
177
+ if ctx.message.base64_attachments == []:
178
+ self.broadcastbot.logger.info(f"Received reaction, sticker or similar from {subscriber_uuid}")
179
+ return
180
+
181
+ # Only attachment, assume the user wants to forward that
182
+ self.broadcastbot.logger.info(f"Received a file from {subscriber_uuid}, broadcasting!")
183
+ await Broadcast.broadcast(self.broadcastbot, ctx)
184
+ return
185
+
186
+ if self.is_valid_command(message, invalid_command=CommandRegex.broadcast):
187
+ return
188
+
189
+ if subscriber_uuid not in self.broadcastbot.subscribers:
190
+ await Subscribe(bot=self.broadcastbot).subscribe(ctx)
191
+
192
+ # By default broadcast all the messages
193
+ await Broadcast.broadcast(self.broadcastbot, ctx)
194
+
195
+
196
+ class DisplayHelp(Command):
197
+ def __init__(self, bot: BroadcasBot) -> None:
198
+ super().__init__()
199
+ self.broadcastbot = bot
200
+
201
+ def _get_help_message(self, input_message: str, subscriber_uuid: str) -> str:
202
+ if input_message.startswith(PublicCommandStrings.help):
203
+ is_wrong_command = False
204
+ else:
205
+ is_wrong_command = True
206
+
207
+ if subscriber_uuid != self.broadcastbot.admin.admin_id:
208
+ if is_wrong_command:
209
+ return self.broadcastbot.wrong_command_message
210
+ else:
211
+ return self.broadcastbot.help_message
212
+ else:
213
+ if is_wrong_command:
214
+ return self.broadcastbot.admin_wrong_command_message
215
+ else:
216
+ return self.broadcastbot.admin_help_message
217
+
218
+ @triggered(CommandRegex.help)
219
+ async def handle(self, ctx: ChatContext) -> None:
220
+ try:
221
+ subscriber_uuid = ctx.message.source_uuid
222
+ message = ctx.message.text
223
+
224
+ help_message = self._get_help_message(message, subscriber_uuid)
225
+
226
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, help_message)
227
+ self.broadcastbot.logger.info(f"Sent help message to {subscriber_uuid}")
228
+ except Exception as e:
229
+ # if isinstance(e, StopPropagation):
230
+ # raise
231
+ self.broadcastbot.logger.error(e, exc_info=True)
232
+
233
+
234
+ class AddAdmin(Command):
235
+ def __init__(self, bot: BroadcasBot) -> None:
236
+ super().__init__()
237
+ self.broadcastbot = bot
238
+
239
+ @triggered(CommandRegex.add_admin)
240
+ async def handle(self, ctx: ChatContext) -> None:
241
+ try:
242
+ subscriber_uuid = ctx.message.source_uuid
243
+ password = self.broadcastbot.message_handler.remove_command_from_message(
244
+ ctx.message.text, AdminCommandStrings.add_admin
245
+ )
246
+
247
+ previous_admin = self.broadcastbot.admin.admin_id
248
+ if await self.broadcastbot.admin.add(subscriber_uuid, password):
249
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "You have been added as admin!")
250
+ if previous_admin is not None and subscriber_uuid != previous_admin:
251
+ msg_to_admin = self.broadcastbot.message_handler.compose_message_to_admin(
252
+ "You are no longer an admin!", subscriber_uuid
253
+ )
254
+ await self.broadcastbot.send(previous_admin, msg_to_admin)
255
+ log_message = f"Previous admin was {previous_admin}, new admin is {subscriber_uuid}"
256
+ self.broadcastbot.logger.info(log_message)
257
+ else:
258
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Adding failed, admin password is incorrect!")
259
+ if previous_admin is not None:
260
+ msg_to_admin = self.broadcastbot.message_handler.compose_message_to_admin(
261
+ "Tried to be added as admin", subscriber_uuid
262
+ )
263
+ await self.broadcastbot.send(previous_admin, msg_to_admin)
264
+ self.broadcastbot.logger.warning(f"{subscriber_uuid} failed password check for add_admin")
265
+ except Exception as e:
266
+ self.broadcastbot.logger.error(e, exc_info=True)
267
+ # finally:
268
+ # raise StopPropagation
269
+
270
+
271
+ class RemoveAdmin(Command):
272
+ def __init__(self, bot: BroadcasBot) -> None:
273
+ super().__init__()
274
+ self.broadcastbot = bot
275
+
276
+ @triggered(CommandRegex.remove_admin)
277
+ async def handle(self, ctx: ChatContext) -> None:
278
+ try:
279
+ subscriber_uuid = ctx.message.source_uuid
280
+ password = self.broadcastbot.message_handler.remove_command_from_message(
281
+ ctx.message.get_body(), AdminCommandStrings.remove_admin
282
+ )
283
+
284
+ previous_admin = self.broadcastbot.admin.admin_id
285
+ if await self.broadcastbot.admin.remove(password):
286
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Admin has been removed!")
287
+ if previous_admin is not None and subscriber_uuid != previous_admin:
288
+ msg_to_admin = self.broadcastbot.message_handler.compose_message_to_admin(
289
+ "You are no longer an admin!", subscriber_uuid
290
+ )
291
+ await self.broadcastbot.send(previous_admin, msg_to_admin)
292
+ self.broadcastbot.logger.info(f"{previous_admin} is no longer an admin")
293
+ else:
294
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Removing failed: admin password is incorrect!")
295
+ if previous_admin is not None:
296
+ msg_to_admin = self.broadcastbot.message_handler.compose_message_to_admin(
297
+ "Tried to remove you as admin", subscriber_uuid
298
+ )
299
+ await self.broadcastbot.send(previous_admin, msg_to_admin)
300
+ self.broadcastbot.logger.warning(f"Failed password check for remove_admin {subscriber_uuid}")
301
+ except Exception as e:
302
+ self.broadcastbot.logger.error(e, exc_info=True)
303
+ # finally:
304
+ # raise StopPropagation
305
+
306
+
307
+ class MessageToAdmin(Command):
308
+ def __init__(self, bot: BroadcasBot) -> None:
309
+ super().__init__()
310
+ self.broadcastbot = bot
311
+
312
+ @triggered(CommandRegex.msg_to_admin)
313
+ async def handle(self, ctx: ChatContext) -> None:
314
+ try:
315
+ subscriber_uuid = ctx.message.source_uuid
316
+ message = self.broadcastbot.message_handler.remove_command_from_message(
317
+ ctx.message.get_body(), PublicCommandStrings.msg_to_admin
318
+ )
319
+
320
+ if self.broadcastbot.admin.admin_id is None:
321
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "I'm sorry but there are no admins to contact!")
322
+ self.broadcastbot.logger.info(f"Tried to contact an admin but there is none! {subscriber_uuid}")
323
+ return
324
+
325
+ if subscriber_uuid in self.broadcastbot.banned_users:
326
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "You are not allowed to contact the admin!")
327
+ self.broadcastbot.logger.info(f"Banned user {subscriber_uuid} tried to contact admin")
328
+ return
329
+
330
+ msg_to_admin = self.broadcastbot.message_handler.compose_message_to_admin(
331
+ "Sent you message:\n", subscriber_uuid
332
+ )
333
+ msg_to_admin += message
334
+ attachments = self.broadcastbot.message_handler.empty_list_to_none(ctx.message.data_message.attachments)
335
+ await self.broadcastbot.send(self.broadcastbot.admin.admin_id, msg_to_admin, attachments=attachments)
336
+ self.broadcastbot.logger.info(
337
+ f"Sent message from {subscriber_uuid} to admin {self.broadcastbot.admin.admin_id}"
338
+ )
339
+ except Exception as e:
340
+ self.broadcastbot.logger.error(e, exc_info=True)
341
+ try:
342
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed to send the message to the admin!")
343
+ except Exception as e:
344
+ self.broadcastbot.logger.error(e, exc_info=True)
345
+ # finally:
346
+ # raise StopPropagation
347
+
348
+
349
+ class MessageFromAdmin(Command):
350
+ def __init__(self, bot: BroadcasBot) -> None:
351
+ super().__init__()
352
+ self.broadcastbot = bot
353
+
354
+ @triggered(CommandRegex.msg_from_admin)
355
+ async def handle(self, ctx: ChatContext) -> None:
356
+ try:
357
+ message = self.broadcastbot.message_handler.remove_command_from_message(
358
+ ctx.message.get_body(), AdminCommandStrings.msg_from_admin
359
+ )
360
+
361
+ if not await self.broadcastbot.is_user_admin(ctx, AdminCommandStrings.msg_from_admin):
362
+ return
363
+
364
+ user_id, message = message.split(" ", 1)
365
+
366
+ if user_id not in self.broadcastbot.subscribers:
367
+ if " " in message:
368
+ confirmation, message = message.split(" ", 1)
369
+ else:
370
+ confirmation = None
371
+ if confirmation != "!force":
372
+ warn_message = "User is not in subscribers list, use !reply <uuid> !force to message them"
373
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, warn_message)
374
+ return
375
+
376
+ message = "Admin: " + message
377
+ attachments = self.broadcastbot.message_handler.empty_list_to_none(ctx.message.data_message.attachments)
378
+
379
+ await self.broadcastbot.send(user_id, message, attachments=attachments)
380
+ self.broadcastbot.logger.info(
381
+ f"Sent message from admin {self.broadcastbot.admin.admin_id} to user {user_id}"
382
+ )
383
+ except Exception as e:
384
+ self.broadcastbot.logger.error(e, exc_info=True)
385
+ try:
386
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed to send the message to the user!")
387
+ except Exception as e:
388
+ self.broadcastbot.logger.error(e, exc_info=True)
389
+ # finally:
390
+ # raise StopPropagation
391
+
392
+
393
+ class BanSubscriber(Command):
394
+ def __init__(self, bot: BroadcasBot) -> None:
395
+ super().__init__()
396
+ self.broadcastbot = bot
397
+
398
+ @triggered(CommandRegex.ban_subscriber)
399
+ async def handle(self, ctx: ChatContext) -> None:
400
+ try:
401
+ user_id = self.broadcastbot.message_handler.remove_command_from_message(
402
+ ctx.message.text, AdminCommandStrings.ban_subscriber
403
+ )
404
+
405
+ if not await self.broadcastbot.is_user_admin(ctx, AdminCommandStrings.ban_subscriber):
406
+ return
407
+
408
+ if user_id in self.broadcastbot.subscribers:
409
+ await self.broadcastbot.subscribers.remove(user_id)
410
+
411
+ user_phone_number = self.broadcastbot.subscribers.get_phone_number(user_id)
412
+ await self.broadcastbot.banned_users.add(user_id, user_phone_number)
413
+
414
+ await self.broadcastbot.send(user_id, "You have been banned")
415
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Successfully banned user")
416
+
417
+ self.broadcastbot.logger.info(f"Banned user {user_id}")
418
+ except Exception as e:
419
+ self.broadcastbot.logger.error(e, exc_info=True)
420
+ try:
421
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed to ban user")
422
+ except Exception as e:
423
+ self.broadcastbot.logger.error(e, exc_info=True)
424
+ # finally:
425
+ # raise StopPropagation
426
+
427
+
428
+ class LiftBanSubscriber(Command):
429
+ def __init__(self, bot: BroadcasBot) -> None:
430
+ super().__init__()
431
+ self.broadcastbot = bot
432
+
433
+ @triggered(CommandRegex.lift_ban_subscriber)
434
+ async def handle(self, ctx: ChatContext) -> None:
435
+ try:
436
+ user_id = self.broadcastbot.message_handler.remove_command_from_message(
437
+ ctx.message.text, AdminCommandStrings.lift_ban_subscriber
438
+ )
439
+
440
+ if not await self.broadcastbot.is_user_admin(ctx, AdminCommandStrings.lift_ban_subscriber):
441
+ return
442
+
443
+ if user_id in self.broadcastbot.banned_users:
444
+ await self.broadcastbot.banned_users.remove(user_id)
445
+ else:
446
+ await self.broadcastbot.reply_with_warn_on_failure(
447
+ ctx, "Could not lift the ban because the user was not banned"
448
+ )
449
+ self.broadcastbot.logger.info(f"Could not lift the ban of {user_id} because the user was not banned")
450
+ return
451
+
452
+ await self.broadcastbot.send(user_id, "You have banned have been lifted, try subscribing again")
453
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Successfully lifted the ban on the user")
454
+
455
+ self.broadcastbot.logger.info(f"Lifted the ban on user {user_id}")
456
+ except Exception as e:
457
+ self.broadcastbot.logger.error(e, exc_info=True)
458
+ try:
459
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed lift the ban on the user")
460
+ except Exception as e:
461
+ self.broadcastbot.logger.error(e, exc_info=True)
462
+ # finally:
463
+ # raise StopPropagation
464
+
465
+
466
+ class SetPing(Command):
467
+ def __init__(self, bot: BroadcasBot) -> None:
468
+ super().__init__()
469
+ self.broadcastbot = bot
470
+
471
+ async def _send_ping(self, ctx: ChatContext) -> None:
472
+ try:
473
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Ping")
474
+ except Exception as e:
475
+ self.broadcastbot.logger.error(e, exc_info=True)
476
+ try:
477
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed to send ping")
478
+ except Exception as e:
479
+ self.broadcastbot.logger.error(e, exc_info=True)
480
+
481
+ @triggered(CommandRegex.set_ping)
482
+ async def handle(self, ctx: ChatContext) -> None:
483
+ try:
484
+ ping_time = self.broadcastbot.message_handler.remove_command_from_message(
485
+ ctx.message.text, AdminCommandStrings.set_ping
486
+ )
487
+
488
+ if not await self.broadcastbot.is_user_admin(ctx, AdminCommandStrings.set_ping):
489
+ return
490
+
491
+ if self.broadcastbot.ping_job is not None:
492
+ self.broadcastbot.scheduler.remove_job(self.broadcastbot.ping_job.id)
493
+ self.broadcastbot.logger.info("Unset old ping job")
494
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Unset old ping job")
495
+
496
+ self.broadcastbot.ping_job = self.broadcastbot.scheduler.add_job(
497
+ self._send_ping, "interval", seconds=int(ping_time), args=[ctx]
498
+ )
499
+
500
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, f"Ping set every {ping_time} seconds")
501
+ self.broadcastbot.logger.info(f"Ping set every {ping_time} seconds")
502
+ except Exception as e:
503
+ self.broadcastbot.logger.error(e, exc_info=True)
504
+ try:
505
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed set ping")
506
+ except Exception as e:
507
+ self.broadcastbot.logger.error(e, exc_info=True)
508
+ # finally:
509
+ # raise StopPropagation
510
+
511
+
512
+ class UnsetPing(Command):
513
+ def __init__(self, bot: BroadcasBot) -> None:
514
+ super().__init__()
515
+ self.broadcastbot = bot
516
+
517
+ @triggered(CommandRegex.unset_ping)
518
+ async def handle(self, ctx: ChatContext) -> None:
519
+ try:
520
+ if not await self.broadcastbot.is_user_admin(ctx, AdminCommandStrings.unset_ping):
521
+ return
522
+
523
+ if self.broadcastbot.ping_job is None:
524
+ await ctx.reply("Cannot unset because ping was not set!")
525
+ return
526
+
527
+ self.broadcastbot.scheduler.remove_job(self.broadcastbot.ping_job.id)
528
+ self.broadcastbot.ping_job = None
529
+ await ctx.reply("Ping unset!")
530
+ except Exception as e:
531
+ self.broadcastbot.logger.error(e, exc_info=True)
532
+ try:
533
+ await self.broadcastbot.reply_with_warn_on_failure(ctx, "Failed to unset ping")
534
+ except Exception as e:
535
+ self.broadcastbot.logger.error(e, exc_info=True)
536
+ # finally:
537
+ # raise StopPropagation