def stars_update(self, event, mid): try: entry = StarboardEntry.select(StarboardEntry, Message).join( Message ).where( (Message.guild_id == event.guild.id) & (StarboardEntry.message_id == mid) ).get() except StarboardEntry.DoesNotExist: return event.msg.reply(':warning: no starboard entry exists with that message id') msg = self.client.api.channels_messages_get( entry.message.channel_id, entry.message_id) users = [i.id for i in msg.get_reactors(STAR_EMOJI)] if set(users) != set(entry.stars): StarboardEntry.update( stars=users, dirty=True ).where( (StarboardEntry.message_id == entry.message_id) ).execute() else: StarboardEntry.update( dirty=True ).where( (StarboardEntry.message_id == mid) ).execute() self.queue_update(event.guild.id, event.config) event.msg.reply(u'Forcing an update on message {}'.format(mid))
def post_star(self, star, source_msg, starboard_id, config): # Generate the embed and post it content, embed = self.get_embed(star, source_msg, config) if not star.star_message_id: try: msg = self.client.api.channels_messages_create(starboard_id, content, embed=embed) except: self.log.exception('Failed to post starboard message: ') return else: try: msg = self.client.api.channels_messages_modify( star.star_channel_id, star.star_message_id, content, embed=embed) except APIException as e: # If we get a 10008, assume this message was deleted if e.code == ERR_UNKNOWN_MESSAGE: star.star_message_id = None star.star_channel_id = None # Recurse so we repost return self.post_star(star, source_msg, starboard_id, config) # Update our starboard entry StarboardEntry.update( dirty=False, star_channel_id=msg.channel_id, star_message_id=msg.id, ).where((StarboardEntry.message_id == star.message_id)).execute()
def force_update_stars(self, event): # First, iterate over stars and repull their reaction count stars = StarboardEntry.select().join(Message).where( (Message.guild_id == event.guild.id) & (~(StarboardEntry.star_message_id >> None))).order_by( Message.timestamp.desc()).limit(100) msg = event.msg.reply('Updating starboard...') for star in stars: info_msg = self.client.api.channels_messages_get( star.message.channel_id, star.message_id) users = [i.id for i in msg.get_reactors(STAR_EMOJI)] if set(users) != set(star.stars): self.log.warning('star %s had outdated reactors list %s vs %s', star.message_id, users, star.stars) StarboardEntry.update( stars=users, dirty=True, ).where( (StarboardEntry.message_id == star.message_id)).execute() info_msg.edit('Starboard updated!') self.queue_update(event.guild.id, event.config)
def post_star(self, star, source_msg, starboard_id, config): # Generate the embed and post it content, embed = self.get_embed(star, source_msg, config) if not star.star_message_id: try: msg = self.client.api.channels_messages_create(starboard_id, content, embed=embed) except: self.log.exception('Failed to post starboard message: ') return else: msg = self.client.api.channels_messages_modify( star.star_channel_id, star.star_message_id, content, embed=embed) # Update our starboard entry StarboardEntry.update( dirty=False, star_channel_id=msg.channel_id, star_message_id=msg.id, ).where((StarboardEntry.message_id == star.message_id)).execute()
def stars_stats(self, event, user=None): if user: try: given_stars = list(StarboardEntry.select( fn.COUNT('*'), ).join(Message).where( (~ (StarboardEntry.star_message_id >> None)) & (StarboardEntry.stars.contains(user.id)) & (Message.guild_id == event.guild.id) ).tuples())[0][0] recieved_stars_posts, recieved_stars_total = list(StarboardEntry.select( fn.COUNT('*'), fn.SUM(fn.array_length(StarboardEntry.stars, 1)), ).join(Message).where( (~ (StarboardEntry.star_message_id >> None)) & (Message.author_id == user.id) & (Message.guild_id == event.guild.id) ).tuples())[0] except: return event.msg.reply(':warning: failed to crunch the numbers on that user') embed = MessageEmbed() embed.color = 0xffd700 embed.title = user.username embed.set_thumbnail(url=user.avatar_url) embed.add_field(name='Total Stars Given', value=str(given_stars), inline=True) embed.add_field(name='Total Posts w/ Stars', value=str(recieved_stars_posts), inline=True) embed.add_field(name='Total Stars Recieved', value=str(recieved_stars_total), inline=True) # embed.add_field(name='Star Rank', value='#{}'.format(recieved_stars_rank), inline=True) return event.msg.reply('', embed=embed) total_starred_posts, total_stars = list(StarboardEntry.select( fn.COUNT('*'), fn.SUM(fn.array_length(StarboardEntry.stars, 1)), ).join(Message).where( (~ (StarboardEntry.star_message_id >> None)) & (Message.guild_id == event.guild.id) ).tuples())[0] top_users = list(StarboardEntry.select(fn.SUM(fn.array_length(StarboardEntry.stars, 1)), User.user_id).join( Message, ).join( User, on=(Message.author_id == User.user_id), ).where( (~ (StarboardEntry.star_message_id >> None)) & (fn.array_length(StarboardEntry.stars, 1) > 0) & (Message.guild_id == event.guild.id) ).group_by(User).order_by(fn.SUM(fn.array_length(StarboardEntry.stars, 1)).desc()).limit(5).tuples()) embed = MessageEmbed() embed.color = 0xffd700 embed.title = 'Star Stats' embed.add_field(name='Total Stars Given', value=total_stars, inline=True) embed.add_field(name='Total Starred Posts', value=total_starred_posts, inline=True) embed.add_field(name='Top Star Recievers', value='\n'.join( '{}. <@{}> ({})'.format(idx + 1, row[1], row[0]) for idx, row in enumerate(top_users) )) event.msg.reply('', embed=embed)
def on_message_reaction_remove_all(self, event): StarboardEntry.update( stars=[], blocked_stars=[], dirty=True ).where( (StarboardEntry.message_id == event.message_id) ).execute() self.queue_update(event.guild.id, event.config)
def post_star(self, star, source_msg, starboard_id, config): # Generate the embed and post it content, embed = self.get_embed(star, source_msg, config) if not source_msg.guild.channels.get(starboard_id): target = self.state.channels.get(starboard_id) if not (target and target.guild): return return self.log.exception(( u'post_star: attempted cross-guild post from ' u'{} ({}) to {} ({}) - #{} ({})'.format( source_msg.guild.name, source_msg.guild.id, target.guild.name, target.guild.id, target.name, target.id ) )) if not star.star_message_id: try: msg = self.client.api.channels_messages_create( starboard_id, content, embed=embed) except: self.log.exception('Failed to post starboard message: ') return else: try: msg = self.client.api.channels_messages_modify( star.star_channel_id, star.star_message_id, content, embed=embed) except APIException as e: # If we get a 10008, assume this message was deleted if e.code == ERR_UNKNOWN_MESSAGE: star.star_message_id = None star.star_channel_id = None # Recurse so we repost return self.post_star(star, source_msg, starboard_id, config) # Update our starboard entry StarboardEntry.update( dirty=False, star_channel_id=msg.channel_id, star_message_id=msg.id, ).where( (StarboardEntry.message_id == star.message_id) ).execute()
def on_message_reaction_add(self, event): try: # Grab the message, and JOIN across blocks to check if a block exists # for either the message author or the reactor. msg = Message.select( Message, StarboardBlock ).join( StarboardBlock, join_type=JOIN.LEFT_OUTER, on=( ( (Message.author_id == StarboardBlock.user_id) | (StarboardBlock.user_id == event.user_id) ) & (Message.guild_id == StarboardBlock.guild_id) ) ).where( (Message.id == event.message_id) ).get() except Message.DoesNotExist: return # If either the reaction or message author is blocked, prevent this action if msg.starboardblock.user_id: event.delete() return # Check if the board prevents self stars sb_id, board = event.config.get_board(event.channel_id) if not sb_id: return if board.prevent_self_star and msg.author_id == event.user_id: event.delete() return try: StarboardEntry.add_star(event.message_id, event.user_id) except peewee.IntegrityError: msg = self.client.api.channels_messages_get( event.channel_id, event.message_id) if msg: Message.from_disco_message(msg) StarboardEntry.add_star(event.message_id, event.user_id) else: return self.queue_update(event.guild.id, event.config)
def stars_unblock(self, event, entity): count = StarboardBlock.delete().where( (StarboardBlock.guild_id == event.guild.id) & (StarboardBlock.entity_id == entity.id)).execute() if not count: raise CommandFail('{} is already allowed on the starboard'.format( entity, )) # Reenable posts and stars for this user StarboardEntry.unblock(entity.id) # Finally, queue an update for the guild self.queue_update(event.guild.id, event.config) raise CommandSuccess('Allowed {} on the starboard'.format(entity, ))
def unlock_stars(self, event, block=False): if event.guild.id not in self.locks: event.msg.reply(':warning: starboard is not locked') return # If the user does not wish to have the messages starred during the lock # duration posted, block them entirely and unflag them as dirty. if block: StarboardEntry.update(dirty=False, blocked=True).join(Message).where( (StarboardEntry.dirty == 1) & (Message.guild_id == event.guild.id) & (Message.timestamp > (datetime.utcnow() - timedelta(hours=32))) ).execute() del self.locks[event.guild.id] event.msg.reply(':white_check_mark: starboard has been unlocked')
def stars_unblock(self, event, user): count = StarboardBlock.delete().where( (StarboardBlock.guild_id == event.guild.id) & (StarboardBlock.user_id == user.id)).execute() if not count: raise CommandFail('{} was not blocked from the starboard'.format( user, )) # Renable posts and stars for this user StarboardEntry.unblock_user(user.id) # Finally, queue an update for the guild self.queue_update(event.guild.id, event.config) raise CommandSuccess('Unblocked {} from the starboard'.format(user, ))
def stars_unblock(self, event, user): count = StarboardBlock.delete().where( (StarboardBlock.guild_id == event.guild.id) & (StarboardBlock.user_id == user.id)).execute() if not count: event.msg.reply(u'{} was not blocked from the starboard'.format( user, )) return # Renable posts and stars for this user StarboardEntry.unblock_user(user.id) # Finally, queue an update for the guild self.queue_update(event.guild.id, event.config, event) event.msg.reply(u'Unblocked {} from the starboard'.format(user, ))
def on_message_update(self, event): sb_id, sb_config = event.config.get_board(event.channel_id) if not sb_id: return count = StarboardEntry.update(dirty=True).where( (StarboardEntry.message_id == event.message.id)).execute() if count: self.queue_update(event.guild.id, event.config)
def delete_star(self, star, update=True): try: self.client.api.channels_messages_delete( star.star_channel_id, star.star_message_id, ) except: pass if update: StarboardEntry.update( dirty=False, star_channel_id=None, star_message_id=None, ).where((StarboardEntry.message_id == star.message_id)).execute() # Update this for post_star star.star_channel_id = None star.star_message_id = None
def stars_block(self, event, user): _, created = StarboardBlock.get_or_create(guild_id=event.guild.id, user_id=user.id, defaults={ 'actor_id': event.author.id, }) if not created: event.msg.reply(u'{} is already blocked from the starboard'.format( user, )) return # Update the starboard, remove stars and posts StarboardEntry.block_user(user.id) # Finally, queue an update for the guild self.queue_update(event.guild.id, event.config) event.msg.reply(u'Blocked {} from the starboard'.format(user, ))
def stars_block(self, event, entity): _, created = StarboardBlock.get_or_create(guild_id=event.guild.id, user_id=entity.id, defaults={ 'actor_id': event.author.id, }) if not created: raise CommandFail( '{} is already not allowed on the starboard'.format(entity, )) # Update the starboard, remove stars and posts StarboardEntry.block(entity.id) # Finally, queue an update for the guild self.queue_update(event.guild.id, event.config) raise CommandSuccess('Disallowed {} from the starboard'.format( entity, ))
def stars_hide(self, event, mid): count = StarboardEntry.update( blocked=True, dirty=True, ).where((StarboardEntry.message_id == mid)).execute() if not count: raise CommandFail('No starred message with that ID') self.queue_update(event.guild.id, event.config) raise CommandSuccess( 'Message {} has been hidden from the starboard'.format(mid, ))
def on_message_delete(self, event): sb_id, sb_config = event.config.get_board(event.channel_id) if not sb_id: return if sb_config.clear_on_delete: stars = list(StarboardEntry.delete().where( (StarboardEntry.message_id == event.id )).returning(StarboardEntry).execute()) for star in stars: self.delete_star(star, update=False)
def stars_hide(self, event, mid): count = StarboardEntry.update( blocked=True, dirty=True, ).where((StarboardEntry.message_id == mid)).execute() if not count: event.msg.reply(u'No starred message with that ID') return self.queue_update(event.guild.id, event.config) event.msg.reply( u'Message {} has been hidden from the starboard'.format(mid, ))
def update_starboard(self, guild_id, config, event): sb_id2, board = config.get_board(event.channel.id) if not sb_id2: return # Grab all dirty stars that where posted in the last 32 hours stars = StarboardEntry.select().join(Message).where( (StarboardEntry.dirty == 1) & (Message.guild_id == guild_id) & (Message.timestamp > (datetime.utcnow() - timedelta(hours=board.max_age)))) for star in stars: sb_id, sb_config = config.get_board(star.message.channel_id) if not sb_id: StarboardEntry.update(dirty=False).where( StarboardEntry.message_id == star.message_id).execute() continue # If this star has no stars, delete it from the starboard if not star.stars: if not star.star_channel_id: StarboardEntry.update( dirty=False).where(StarboardEntry.message_id == star.message_id).execute() continue self.delete_star(star) continue # Grab the original message try: source_msg = self.client.api.channels_messages_get( star.message.channel_id, star.message_id) except: self.log.exception('Star message went missing %s / %s: ', star.message.channel_id, star.message_id) # TODO: really delete this self.delete_star(star, update=True) continue # If we previously posted this in the wrong starboard, delete it if star.star_channel_id and (star.star_channel_id != sb_id or len( star.stars) < sb_config.min_stars) or star.blocked: self.delete_star(star, update=True) if len(star.stars) < sb_config.min_stars or star.blocked: StarboardEntry.update(dirty=False).where( StarboardEntry.message_id == star.message_id).execute() continue self.post_star(star, source_msg, sb_id, sb_config)
def stars_show(self, event, mid): try: star = StarboardEntry.select().join(Message).where( (Message.guild_id == event.guild.id) & (~(StarboardEntry.star_message_id >> None)) & ((Message.id == mid) | (StarboardEntry.star_message_id == mid))).get() except StarboardEntry.DoesNotExist: raise CommandFail('no starboard message with that id') _, sb_config = event.config.get_board(star.message.channel_id) try: source_msg = self.client.api.channels_messages_get( star.message.channel_id, star.message_id) except: raise CommandFail('no starboard message with that id') content, embed = self.get_embed(star, source_msg, sb_config) event.msg.reply(content, embed=embed)
def on_message_reaction_remove(self, event): StarboardEntry.remove_star(event.message_id, event.user_id) self.queue_update(event.guild.id, event.config)
def stars_update(self, event, mid): StarboardEntry.update(dirty=True).where( (StarboardEntry.message_id == mid)).execute() self.queue_update(event.guild.id, event.config) event.msg.reply(u'Forcing an update on message {}'.format(mid))