async def remind( self, ctx, execute_at: interpret_str_as_datetime, *, content: commands.clean_content(fix_channel_mentions=True) ): if len(content) > Reminder.MAX_CONTENT_LENGTH: raise commands.BadArgument description = ( f'**Przypomnę ci tutaj "{content}" {human_datetime(execute_at)}.**\n*Przypomnienie zostanie anulowane ' 'jeśli usuniesz tę wiadomość. Możesz to zrobić przy użyciu komendy `nie`.*' ) embed = self.bot.generate_embed('🍅', 'Ustawiono przypomnienie', description) confirmation_message = await self.bot.send(ctx, embed=embed) if confirmation_message is None: return try: details = { 'confirmation_message_id': confirmation_message.id, 'channel_id': ctx.channel.id, 'content': content, 'user_id': ctx.author.id, 'requested_at': utc_to_naive_local(ctx.message.created_at), 'execute_at': execute_at } with data.session(commit=True) as session: reminder = Reminder(**details) session.add(reminder) self.bot.loop.create_task(self.set_off_reminder(**details)) except: await confirmation_message.delete() raise
async def on_ready(self): psutil.cpu_percent() localize() self.ready_datetime = dt.datetime.now() self.session = aiohttp.ClientSession(loop=self.loop, headers=self.HEADERS) self.ch_client = ChClient( self.session, url=configuration["clickhouse_url"], user=configuration["clickhouse_user"], password=configuration["clickhouse_password"], database="somsiad", ) assert await self.ch_client.is_alive() print('Przygotowywanie danych serwerów...') data.Server.register_all(self.guilds) with data.session(commit=True) as session: for server in session.query(data.Server): self.prefixes[server.id] = tuple(server.command_prefix.split('|')) if server.command_prefix else () if server.joined_at is None: discord_server = self.get_guild(server.id) if discord_server is not None and discord_server.me is not None: server.joined_at = utc_to_naive_local(discord_server.me.joined_at) self.system_channel = cast(Optional[discord.TextChannel], await self.fetch_channel(517422572615499777)) # magic self.public_channel = cast(Optional[discord.TextChannel], await self.fetch_channel(479458695126974466)) # magic self.loop.create_task(self.cycle_presence()) await self.system_notify('✅', 'Włączyłem się') self.print_info()
async def burn(self, ctx, execute_at: interpret_str_as_datetime): """Removes the message after a specified mount time.""" confirmation_description = ( md_link(f'**Zostanie ona usunięta {human_datetime(execute_at)}.**', ctx.message.jump_url) + '\n*Spalenie zostanie anulowane jeśli usuniesz tę wiadomość. Możesz to zrobić przy użyciu komendy `nie`.*' ) confirmation_embed = self.bot.generate_embed('🔥', f'Spalę twoją wiadomość', confirmation_description) confirmation_message = await self.bot.send(ctx, embed=confirmation_embed) if confirmation_message is None: return try: details = { 'confirmation_message_id': confirmation_message.id, 'target_message_id': ctx.message.id, 'channel_id': ctx.channel.id, 'user_id': ctx.author.id, 'requested_at': utc_to_naive_local(ctx.message.created_at), 'execute_at': execute_at, } with data.session(commit=True) as session: reminder = Burning(**details) session.add(reminder) self.bot.loop.create_task(self.set_off_burning(**details)) except: await confirmation_message.delete() raise
async def on_message(self, message: discord.Message): if not message.attachments or message.guild is None: return images9000 = [] for attachment in message.attachments: if attachment.height and attachment.width: image_bytes = io.BytesIO() try: await attachment.save(image_bytes) except (discord.HTTPException, discord.NotFound): continue else: try: hash_string = self._hash(image_bytes) except: continue images9000.append( Image9000( attachment_id=attachment.id, message_id=message.id, user_id=message.author.id, channel_id=message.channel.id, server_id=message.guild.id, hash=hash_string, sent_at=utc_to_naive_local(message.created_at), )) try: with data.session(commit=True) as session: session.bulk_save_objects(images9000) except (psycopg2.errors.ForeignKeyViolation, psycopg2.errors.UniqueViolation): pass
async def on_command(self, ctx: commands.Context): with data.session(commit=True) as session: invocation = Invocation( message_id=ctx.message.id, server_id=ctx.guild.id if ctx.guild is not None else None, channel_id=ctx.channel.id, user_id=ctx.author.id, prefix=ctx.prefix, full_command=ctx.command.qualified_name, root_command=str(ctx.command.root_parent or ctx.command.qualified_name), created_at=utc_to_naive_local(ctx.message.created_at)) session.add(invocation)
async def vote( self, ctx, conclude_at: Optional[interpret_str_as_datetime] = None, *, matter: commands.clean_content(fix_channel_mentions=True), ): if len(matter) > Ballot.MAX_MATTER_LENGTH: raise commands.BadArgument letters = ''.join( {match[0]: None for match in self.LETTER_REGEX.findall(matter)}) if len(letters) < 2: letters = None description = 'Zagłosuj w tej sprawie przy użyciu reakcji.' if conclude_at is not None: description += ( f'\n**Wyniki zostaną ogłoszone {human_datetime(conclude_at)}.**\n*Ogłoszenie wyników zostanie ' 'anulowane jeśli usuniesz tę wiadomość. Możesz to zrobić przy użyciu komendy `nie`.*' ) embed = self.bot.generate_embed('🗳', matter, description) urn_message = await self.bot.send(ctx, embed=embed) if urn_message is None: return options = ('👍', '👎') if letters is None else tuple( map(self.LETTER_EMOJIS.get, letters)) try: for option in options: await urn_message.add_reaction(option) details = { 'urn_message_id': urn_message.id, 'channel_id': ctx.channel.id, 'matter': matter, 'letters': letters, 'user_id': ctx.author.id, 'commenced_at': utc_to_naive_local(ctx.message.created_at), 'conclude_at': conclude_at, } if conclude_at is not None: with data.session(commit=True) as session: reminder = Ballot(**details) session.add(reminder) self.bot.loop.create_task(self.set_off_ballot(**details)) except discord.Forbidden: await urn_message.delete() embed = self.bot.generate_embed( '⚠️', 'Bot nie ma uprawnień do dodawania reakcji') except: await urn_message.delete() raise
async def _update_metadata_cache(self, channel: discord.TextChannel, session: data.RawSession): try: self.relevant_channel_stats[channel.id] = self.relevant_channel_stats.default_factory() relevant_message_metadata = session.query(MessageMetadata).filter( MessageMetadata.channel_id == channel.id ).order_by(MessageMetadata.id.desc()) last_cached_message_metadata = relevant_message_metadata.first() if last_cached_message_metadata is not None: after = discord.utils.snowflake_time(last_cached_message_metadata.id) else: after = None metadata_cache_update = [] async for message in channel.history(limit=None, after=after): if message.type == discord.MessageType.default: message_datetime = utc_to_naive_local(message.created_at) content_parts = [message.clean_content] for embed in message.embeds: content_parts.append(embed.title) content_parts.append(embed.description) for field in embed.fields: content_parts.append(field.name) content_parts.append(field.value) if embed.footer: content_parts.append(embed.footer.text) if embed.author: content_parts.append(embed.author.text) content = ' '.join(filter(None, content_parts)) message_metadata = MessageMetadata( id=message.id, server_id=message.guild.id, channel_id=channel.id, user_id=message.author.id, word_count=len(content.split()), character_count=len(content), hour=message_datetime.hour, weekday=message_datetime.weekday(), datetime=message_datetime ) metadata_cache_update.append(message_metadata) self.messages_cached += 1 if self.messages_cached % 10_000 == 0: await self._send_or_update_progress() if len(metadata_cache_update) % self.STEP == 0: metadata_cache_update.reverse() session.bulk_save_objects(metadata_cache_update) session.commit() metadata_cache_update = [] metadata_cache_update.reverse() session.bulk_save_objects(metadata_cache_update) session.commit()
async def on_ready(self): psutil.cpu_percent() localize() self.run_datetime = dt.datetime.now() self.session = aiohttp.ClientSession(loop=self.loop, headers=self.HEADERS) print('Przygotowywanie danych serwerów...') data.Server.register_all(self.guilds) with data.session(commit=True) as session: for server in session.query(data.Server): self.prefixes[server.id] = tuple( server.command_prefix.split( '|')) if server.command_prefix else () if server.joined_at is None: discord_server = self.get_guild(server.id) if discord_server is not None and discord_server.me is not None: server.joined_at = utc_to_naive_local( discord_server.me.joined_at) self.loop.create_task(self.cycle_presence()) self.print_info()
def _plot_activity_by_channel(self, ax): # plot the chart channels = [self.ctx.bot.get_channel(channel) for channel in self.relevant_channel_stats] channel_names = [f'#{channel}' for channel in channels] channel_existence_lengths = ( (self.timeframe_end_date - utc_to_naive_local(channel.created_at).date()).days + 1 for channel in channels ) if self.last_days: channel_existence_lengths = ( min(self.last_days, channel_existence_length) for channel_existence_length in channel_existence_lengths ) average_daily_message_counts = [ channel_stats['message_count'] / channel_existence_length for channel_stats, channel_existence_length in zip(self.relevant_channel_stats.values(), channel_existence_lengths) ] ax.bar( channel_names, average_daily_message_counts, color=self.BACKGROUND_COLOR, facecolor=self.FOREGROUND_COLOR, width=1 ) # set proper X axis formatting ax.set_xlim(-0.5, len(channel_names)-0.5) ax.set_xticklabels(channel_names, rotation=30, ha='right') # set proper ticker intervals on the Y axis accounting for the maximum number of messages ax.yaxis.set_major_locator(ticker.MaxNLocator(nbins='auto', steps=[10], integer=True)) if max(average_daily_message_counts) >= 10: ax.yaxis.set_minor_locator(ticker.AutoMinorLocator(n=10)) # make it look nice ax.set_facecolor(self.BACKGROUND_COLOR) ax.set_xlabel('Kanał', color=self.FOREGROUND_COLOR, fontsize=11, fontweight='bold') ax.set_ylabel( 'Średnio wysłanych\nwiadomości dziennie', color=self.FOREGROUND_COLOR, fontsize=11, fontweight='bold' ) return ax
async def _fill_in_details(self): timeframe_start_date_utc = None if self.user_by_id: try: self.subject = await self.ctx.bot.fetch_user(self.subject_id) except discord.NotFound: self.type = self.Type.DELETED_USER self._generate_relevant_embed = self._generate_deleted_user_embed else: self.type = self.Type.USER self._generate_relevant_embed = self._generate_user_embed elif isinstance(self.subject, discord.Guild): self.type = self.Type.SERVER self._generate_relevant_embed = self._generate_server_embed timeframe_start_date_utc = self.subject.created_at elif isinstance(self.subject, discord.TextChannel): self.type = self.Type.CHANNEL # raise an exception if the requesting user doesn't have access to the channel if not self.subject.permissions_for(self.ctx.author).read_messages: raise commands.BadArgument self._generate_relevant_embed = self._generate_channel_embed timeframe_start_date_utc = self.subject.created_at elif isinstance(self.subject, discord.CategoryChannel): self.type = self.Type.CATEGORY # raise an exception if the requesting user doesn't have access to the channel if not self.subject.permissions_for(self.ctx.author).read_messages: raise commands.BadArgument self._generate_relevant_embed = self._generate_category_embed timeframe_start_date_utc = self.subject.created_at elif isinstance(self.subject, discord.Member): self.type = self.Type.MEMBER self._generate_relevant_embed = self._generate_member_embed timeframe_start_date_utc = self.subject.joined_at elif isinstance(self.subject, discord.User): self.type = self.Type.USER self._generate_relevant_embed = self._generate_user_embed if timeframe_start_date_utc is not None: self.timeframe_start_date = utc_to_naive_local(timeframe_start_date_utc).date()