async def test_explicit_initial_runs_tomorrow_multi(): now = utils.utcnow() if not ((0, 4) < (now.hour, now.minute) < (23, 59)): await asyncio.sleep(5 * 60) # sleep for 5 minutes now = utils.utcnow() # multiple times that are in the past for today times = [] for _ in range(3): now -= datetime.timedelta(minutes=1) times.append(datetime.time(hour=now.hour, minute=now.minute)) has_run = False async def inner(): nonlocal has_run has_run = True # a loop that should have an initial run tomorrow loop = tasks.loop(time=times)(inner) loop.start() await asyncio.sleep(1) try: assert not has_run finally: loop.cancel()
async def test_explicit_initial_runs_tomorrow_single(): now = utils.utcnow() if not ((0, 4) < (now.hour, now.minute) < (23, 59)): await asyncio.sleep(5 * 60) # sleep for 5 minutes now = utils.utcnow() has_run = False async def inner(): nonlocal has_run has_run = True time = utils.utcnow() - datetime.timedelta(minutes=1) # a loop that should have an initial run tomorrow loop = tasks.loop(time=datetime.time(hour=time.hour, minute=time.minute))(inner) loop.start() await asyncio.sleep(1) try: assert not has_run finally: loop.cancel()
async def mod_loop(self): guild: Guild = self.bot.guilds[0] async for ban in await db.stream(filter_by(Ban, active=True)): if ban.days != -1 and utcnow() >= ban.timestamp + timedelta( days=ban.days): await Ban.deactivate(ban.id) try: user = await self.bot.fetch_user(ban.member) except NotFound: user = ban.member, ban.member_name if isinstance(user, User): try: await guild.unban(user) except Forbidden: await send_alert( guild, t.cannot_unban_user_permissions( user.mention, user.id)) await send_to_changelog_mod(guild, None, Colors.unban, t.log_unbanned, user, t.log_unbanned_expired) mute_role: Optional[Role] = guild.get_role(await RoleSettings.get("mute")) if mute_role is None: return try: check_role_assignable(mute_role) except CommandError: await send_alert( guild, t.cannot_assign_mute_role(mute_role, mute_role.id)) return async for mute in await db.stream(filter_by(Mute, active=True)): if mute.days != -1 and utcnow() >= mute.timestamp + timedelta( days=mute.days): if member := guild.get_member(mute.member): await member.remove_roles(mute_role) else: member = mute.member, mute.member_name await send_to_changelog_mod(guild, None, Colors.unmute, t.log_unmuted, member, t.log_unmuted_expired) await Mute.deactivate(mute.id)
async def create_and_start_event(bot: Bot): event_name = Plugin.get_cvar("qlx_discord_ext_event_name") for scheduled_event in bot.guilds[0].scheduled_events: if event_name in scheduled_event.name and scheduled_event.status == EventStatus.active: return event_location = Plugin.get_cvar("qlx_discord_ext_event_location") start_date = utcnow() + timedelta(seconds=1) end_date = utcnow() + timedelta(hours=8) await bot.guilds[0].create_scheduled_event( name=event_name, privacy_level=PrivacyLevel.guild_only, start_time=start_date, end_time=end_date, entity_type=EntityType.external, location=event_location)
async def create(member_id: int, author_id: int, content: str) -> UserNote: row = UserNote(member_id=member_id, author_id=author_id, content=content, timestamp=utcnow()) await db.add(row) return row
async def create_boss_embed(self, bangs=0, boss_message=None): boss_life = self.config()['required_bangs'] new_embed = discord.Embed( title=random.choice([ "A duck boss is here...", "A wild boss has appeared...", "A boss has spawned...", "KILL THE BOSS !", "Who wants some foie gras ?", "There is a Duck Boss nearby...", "You cannot sleep when enemies are nearby." ]), color=discord.Color.green(), description="React with 🔫 to kill it.", ) new_embed.set_image( url= "https://media.discordapp.net/attachments/795225915248214036/795404123443953705/boss_Calgeka.png" ) new_embed.add_field(name="Health", value=f"{boss_life - bangs}/{boss_life}") if boss_message: time_delta = utcnow() - boss_message.created_at new_embed.set_footer( text= f"The boss spawned {format_timedelta(time_delta, locale='en_US')} ago" ) else: new_embed.set_footer(text=f"The boss just spawned") return new_embed
async def create(member_id: int, channel_id: int) -> DynChannelMember: member = DynChannelMember(id=str(uuid4()), member_id=member_id, channel_id=channel_id, timestamp=utcnow()) await db.add(member) return member
class HeartbeatCog(Cog, name="Heartbeat"): CONTRIBUTORS = [Contributor.Defelo, Contributor.wolflu] def __init__(self): super().__init__() self.initialized = False def get_owner(self) -> Optional[User]: return self.bot.get_user(OWNER_ID) @tasks.loop(seconds=20) async def status_loop(self): if (owner := self.get_owner()) is None: return try: await send_editable_log( owner, t.online_status, t.status_description(Config.NAME, Config.VERSION), t.heartbeat, format_dt(now := utcnow(), style="D") + " " + format_dt(now, style="T"), ) Path("health").write_text(str(int(datetime.now().timestamp()))) except Forbidden: pass
async def create(member: int, member_name: str, channel: int) -> MediaOnlyDeletion: row = MediaOnlyDeletion(member=member, member_name=member_name, timestamp=utcnow(), channel=channel) await db.add(row) return row
async def create(regex: str, description: str, delete: bool) -> BadWord: row = BadWord(regex=regex, description=description, delete=delete, timestamp=utcnow()) await db.add(row) await sync_redis() return row
async def deactivate(mute_id: int, unmute_mod: int = None, reason: str = None) -> "Mute": row: Mute = await db.get(Mute, id=mute_id) row.active = False row.deactivation_timestamp = utcnow() row.unmute_mod = unmute_mod row.unmute_reason = reason return row
async def create(member: int, member_name: str, mod: int, reason: str) -> Warn: row = Warn(member=member, member_name=member_name, mod=mod, timestamp=utcnow(), reason=reason) await db.add(row) return row
async def create(member: int, member_name: str, reporter: int, reason: str) -> Report: row = Report(member=member, member_name=member_name, reporter=reporter, timestamp=utcnow(), reason=reason) await db.add(row) return row
async def deactivate(ban_id: int, unban_mod: int = None, unban_reason: str = None) -> Ban: row: Ban = await db.get(Ban, id=ban_id) row.active = False row.deactivation_timestamp = utcnow() row.unban_mod = unban_mod row.unban_reason = unban_reason return row
async def create(member: int, member_name: str, mod: Optional[int], reason: Optional[str]) -> Kick: row = Kick(member=member, member_name=member_name, mod=mod, timestamp=utcnow(), reason=reason) await db.add(row) return row
async def update_msg(m: Message, content): embed.description = content embed.timestamp = utcnow() await ignore_message_edit(m) try: await m.edit(embed=embed) except NotFound: return await reply(ctx, embed=embed) return m
async def create(member: int, member_name: str, channel: int, name: str) -> IllegalInvitePost: row = IllegalInvitePost(member=member, member_name=member_name, timestamp=utcnow(), channel=channel, name=name) await db.add(row) return row
async def update_progress_message(): while len(completed) < len(channels): content = t.scanning_channel(len(completed), len(channels), cnt=len(active)) for a, d in active.items(): channel_age = (utcnow() - a.created_at).days content += f"\n:small_orange_diamond: {a.mention} ({d} / {min(channel_age, days)})" message[0] = await update_msg(message[0], content) await asyncio.sleep(2)
async def handle_get_user_status_entries( self, user_id: int) -> list[tuple[str, str]]: status = t.none if (ban := await db.get(Ban, member=user_id, active=True)) is not None: if ban.days != -1: expiry_date: datetime = ban.timestamp + timedelta( days=ban.days) days_left = (expiry_date - utcnow()).days + 1 status = t.status_banned_days(cnt=ban.days, left=days_left) else: status = t.status_banned
async def create(guild_id: int, guild_name: str, applicant: int, mod: int, approved: bool) -> InviteLog: row = InviteLog( guild_id=guild_id, guild_name=guild_name, applicant=applicant, mod=mod, timestamp=utcnow(), approved=approved, ) await db.add(row) return row
async def create(member: int, member_name: str, channel: int, content: str, deleted: bool) -> BadWordPost: row = BadWordPost( member=member, member_name=member_name, channel=channel, content=content, deleted_message=deleted, timestamp=utcnow(), ) await db.add(row) return row
def test_task_is_imaginary(): import zoneinfo tz = zoneinfo.ZoneInfo('America/New_York') # 2:30 AM was skipped dt = datetime.datetime(2022, 3, 13, 2, 30, tzinfo=tz) assert tasks.is_imaginary(dt) now = utils.utcnow() # UTC time is never imaginary or ambiguous assert not tasks.is_imaginary(now)
def test_task_is_ambiguous(): import zoneinfo tz = zoneinfo.ZoneInfo('America/New_York') # 1:30 AM happened twice dt = datetime.datetime(2022, 11, 6, 1, 30, tzinfo=tz) assert tasks.is_ambiguous(dt) now = utils.utcnow() # UTC time is never imaginary or ambiguous assert not tasks.is_imaginary(now)
async def create(guild_id: int, code: str, guild_name: str, applicant: int, approver: int) -> AllowedInvite: row = AllowedInvite( guild_id=guild_id, code=code, guild_name=guild_name, applicant=applicant, approver=approver, description=None, created_at=utcnow(), ) await db.add(row) return row
async def update_members(c: TextChannel): active[c] = 0 async for msg in c.history(limit=None, oldest_first=False): s = (utcnow() - msg.created_at).total_seconds() if s > days * 24 * 60 * 60: break members[msg.author] = max(members.get(msg.author, msg.created_at), msg.created_at) active[c] = int(s / (24 * 60 * 60)) del active[c] completed.append(c)
async def on_ready(self): if (owner := self.get_owner()) is not None: try: await send_editable_log( owner, t.online_status, t.status_description(Config.NAME, Config.VERSION), t.logged_in, format_dt(now := utcnow(), style="D") + " " + format_dt(now, style="T"), force_resend=True, force_new_embed=not self.initialized, ) except Forbidden: pass
async def handle_get_user_status_entries(self, user_id) -> list[tuple[str, str]]: inactive_days = await InactivitySettings.inactive_days.get() activity: Optional[Activity] = await db.get(Activity, id=user_id) if activity is None: status = t.status.inactive elif (utcnow() - activity.timestamp).days >= inactive_days: status = t.status.inactive_since( format_dt(activity.timestamp, style="R")) else: status = t.status.active(format_dt(activity.timestamp, style="R")) return [(t.activity, status)]
async def send_poll(ctx: Context, title: str, args: str, field: Optional[Tuple[str, str]] = None, allow_delete: bool = True): question, *options = [ line.replace("\x00", "\n") for line in args.replace("\\\n", "\x00").split("\n") if line ] if not options: raise CommandError(t.missing_options) if len(options) > MAX_OPTIONS - allow_delete: raise CommandError(t.too_many_options(MAX_OPTIONS - allow_delete)) options = [PollOption(ctx, line, i) for i, line in enumerate(options)] if any(len(str(option)) > EmbedLimits.FIELD_VALUE for option in options): raise CommandError(t.option_too_long(EmbedLimits.FIELD_VALUE)) embed = Embed(title=title, description=question, color=Colors.Polls, timestamp=utcnow()) embed.set_author(name=str(ctx.author), icon_url=ctx.author.display_avatar.url) if allow_delete: embed.set_footer(text=t.created_by(ctx.author, ctx.author.id), icon_url=ctx.author.display_avatar.url) if len(set(map(lambda x: x.emoji, options))) < len(options): raise CommandError(t.option_duplicated) for option in options: embed.add_field(name="** **", value=str(option), inline=False) if field: embed.add_field(name=field[0], value=field[1], inline=False) poll: Message = await ctx.send(embed=embed) try: for option in options: await poll.add_reaction(option.emoji) if allow_delete: await poll.add_reaction(name_to_emoji["wastebasket"]) except Forbidden: raise CommandError(t.could_not_add_reactions(ctx.channel.mention))
async def cleanup_loop(self): days: int = await LoggingSettings.maxage.get() if days == -1: return timestamp = utcnow() - timedelta(days=days) for setting in [ LoggingSettings.edit_channel, LoggingSettings.delete_channel ]: channel: Optional[TextChannel] = await self.get_logging_channel( setting) if channel is None: continue async for message in channel.history( limit=None, oldest_first=True): # type: Message if message.created_at > timestamp: break await message.delete()
async def send_to_changelog_mod( guild: Guild, message: Optional[Message], colour: int, title: str, member: Union[Member, User, Tuple[int, str]], reason: str, *, duration: Optional[str] = None, ): embed = Embed(title=title, colour=colour, timestamp=utcnow()) if isinstance(member, tuple): member_id, member_name = member embed.set_author(name=member_name) else: member_id: int = member.id member_name: str = str(member) embed.set_author(name=member_name, icon_url=member.display_avatar.url) embed.add_field(name=t.log_field.member, value=f"<@{member_id}>", inline=True) embed.add_field(name=t.log_field.member_id, value=str(member_id), inline=True) if message: embed.set_footer(text=str(message.author), icon_url=message.author.display_avatar.url) embed.add_field(name=t.log_field.channel, value=t.jump_url(message.channel.mention, message.jump_url), inline=True) if duration: embed.add_field(name=t.log_field.duration, value=duration, inline=True) embed.add_field(name=t.log_field.reason, value=reason, inline=False) await send_to_changelog(guild, embed)