async def fetch(bot, ctx, allow_started=False, guild_id = None): guild = await bot.fetch_guild_from_ctx(ctx, guild_id) cc = await guild.fetch_current_challenge() BotErr.raise_if(cc is None, 'Create a new challenge first.') BotErr.raise_if(cc is not None and not allow_started and await cc.has_started(), 'Cannot add/delete user/title/pool after a challenge has started.') return State(bot, guild, cc)
async def rename_pool(self, ctx, old_name, new_name): state = await State.fetch(self, ctx, allow_started=True) BotErr.raise_if(await state.cc.has_pool(new_name), f'Pool "{new_name}" already exists.') pool = await state.fetch_pool(old_name) pool.name = new_name await pool.update() await self.db.commit()
async def unban_user(self, ctx, user): state = await State.fetch(self, ctx, allow_started=True) u = await state.fetch_user(user) BotErr.raise_if(not await state.is_user_banned(u), f'User {user.mention} is not banned') await state.cc.remove_banned_user(u) await self.db.commit()
async def rename_title(self, ctx, old_name, new_name): state = await State.fetch(self, ctx) BotErr.raise_if(await state.cc.has_title(new_name), f'Title "{new_name}" already exists.') title = await state.fetch_title(old_name) title.name = new_name await title.update() await self.db.commit()
async def remove_title(self, ctx, name): state = await State.fetch(self, ctx) title = await state.fetch_title(name) BotErr.raise_if(title.is_used, "Cannot delete title that's already been used.") await title.delete() await self.db.commit()
async def ban_user(self, ctx, user): state = await State.fetch(self, ctx, allow_started=True) u = await state.fetch_user(user) BotErr.raise_if(await state.is_user_banned(u), f'User {user.mention} has already been banned') await state.cc.add_banned_user(u) await self.db.commit()
async def start_round(self, ctx, days, pool): state = await State.fetch(self, ctx, allow_started=True) last_round = await state.cc.fetch_last_round() if last_round is not None and not last_round.is_finished: raise BotErr(f'Finish round {last_round.num} first.') pool = await state.fetch_pool(pool) users_participants = await state.cc.fetch_users_participants() users = { up[0].id: up[0] for up in users_participants } participants = [ up[1] for up in filter(lambda up: not up[1].has_failed(), users_participants) ] BotErr.raise_if(len(participants) == 0, 'Not enough participants to start a round.') titles = await pool.fetch_unused_titles() BotErr.raise_if(len(titles) < len(participants), f'Not enough titles in "{pool}" pool.') num = last_round.num + 1 if last_round is not None else 0 start = datetime.now() new_round = await state.cc.add_round(num, start, start + timedelta(days=days)) rand_titles = [ titles.pop(random.randrange(len(titles))) for _ in range(len(participants)) ] for participant, title in zip(participants, rand_titles): await new_round.add_roll(participant.id, title.id) participant.progress_current = None participant.progress_total = None title.is_used = True await participant.update() await title.update() await self.db.commit() return new_round, { users[p.user_id].name: t.name for p, t in zip(participants, rand_titles) }
async def sync_all(self, ctx): guild = await Guild.fetch_or_insert(self.db, ctx.message.guild.id) # todo: move logic? challenges = await guild.fetch_challenges() BotErr.raise_if(guild.spreadsheet_key is None, 'Spreadsheet key is not set.') # todo: maybe its bad to have single # spreadsheet_key per guild, maybe # we need to store it in challange column for c in challenges: await export(guild.spreadsheet_key, c)
async def fetch(bot, ctx, allow_started=False): guild = await Guild.fetch_or_insert(bot.db, ctx.message.guild.id) cc = await guild.fetch_current_challenge() BotErr.raise_if(cc is None, 'Create a new challenge first.') BotErr.raise_if( cc is not None and not allow_started and await cc.has_started(), 'Cannot add/delete user/title/pool after a challenge has started.') return State(bot, guild, cc)
async def add_user(self, ctx, user): state = await State.fetch(self, ctx) BotErr.raise_if( await state.has_participant(user), f'User {user.mention} is already participating in this challenge.') user = await state.fetch_user(user) await state.cc.add_participant(user.id) await self.db.commit()
async def add_title(self, ctx, pool, user, name, url): state = await State.fetch(self, ctx) BotErr.raise_if(await state.cc.has_title(name), f'Title "{name}" already exists.') participant = await state.fetch_participant(user) pool = await state.fetch_pool(pool) await pool.add_title(participant.id, name, url) await self.db.commit()
async def fetch_participant(self, user): u = await self.fetch_user(user) p = await self.cc.fetch_participant(u.id) BotErr.raise_if( p is None, f'User {user.mention} is not participating in this challenge.') BotErr.raise_if(p.has_failed(), f'User {user.mention} has failed this challenge.') return p
async def remove_title(self, ctx, name, is_admin=False): state = await State.fetch(self, ctx, allow_started=is_admin) title = await state.fetch_title(name) participant = await state.fetch_participant(ctx.message.author) BotErr.raise_if(participant.id != title.participant_id and not is_admin, "Can't remove other's title") BotErr.raise_if(title.is_used, "Cannot delete title that's already been used.") await title.delete() await self.db.commit()
async def fetch_last_round(self, allow_past_deadline=False): lr = await self.cc.fetch_last_round() BotErr.raise_if(lr is None, 'Create a new round first.') BotErr.raise_if( lr is not None and (lr.is_finished or not allow_past_deadline and datetime.now() > lr.finish_time), 'Round has ended.') return lr
async def _set_title(self, roll, new_title): BotErr.raise_if(new_title.is_used, f'Title "{new_title.name}" is already used.') old_title = await roll.fetch_title() old_title.is_used = False roll.title_id = new_title.id new_title.is_used = True await old_title.update() await roll.update() await new_title.update()
async def start_challenge(self, ctx, name): guild = await Guild.fetch_or_insert(self.db, ctx.message.guild.id) if guild.current_challenge_id is not None: raise BotErr(f'Finish "{(await guild.fetch_current_challenge()).name}" challenge first.') BotErr.raise_if(await guild.has_challenge(name), f'Challenge "{name}" already exists.') challenge = await guild.add_challenge(name, datetime.now()) await challenge.add_pool('main') guild.current_challenge_id = challenge.id await guild.update() await self.db.commit()
async def reroll(self, ctx, user, pool): state = await State.fetch(self, ctx, allow_started=True) last_round = await state.fetch_last_round() participant = await state.fetch_participant(user) roll = await last_round.fetch_roll(participant.id) pool = await state.fetch_pool(pool) titles = await pool.fetch_unused_titles() BotErr.raise_if(len(titles) == 0, f'Not enough titles in "{pool}" pool.') new_title = random.choice(titles) await self._set_title(roll, new_title) await self.db.commit() return new_title
async def fetch_guild_from_ctx(self, ctx, guild_id): guild = ctx.message.guild if guild: guild = await Guild.fetch_or_insert(self.db, guild.id) else: # we're in dms, so we try to deduce the guild from db author = ctx.message.author user = await User.fetch_or_insert(self.db, author.id, author.name) active_guilds = await user.fetch_active_guilds() if guild_id == None and len(active_guilds) > 1: raise GuildAmbiguity(active_guilds) BotErr.raise_if(len(active_guilds) == 0, 'No active guilds') guild = active_guilds[0] if guild_id: guild = [ g for g in active_guilds if g.id == guild_id ][0] return guild
async def add_title(self, ctx, params, is_admin=False): # ? maybe move it into a class state = await State.fetch(self, ctx, guild_id = params['guild_id'], allow_started=is_admin) name = params['title_name'] user = params['user'] pool = params['pool'] url = params['url'] if 'is_hidden' in params: is_hidden = params['is_hidden'] else: is_hidden = False score = params['score'] duration = params['duration'] num_of_episodes = params['num_of_episodes'] difficulty = params['difficulty'] BotErr.raise_if(await state.cc.has_title(name), f'Title "{name}" already exists.') participant = await state.fetch_participant(user) pool = await state.fetch_pool(pool) await pool.add_title(participant.id, name, url, score, num_of_episodes, duration, difficulty, is_hidden) await self.db.commit()
async def add_user(self, ctx, user): state = await State.fetch(self, ctx) u = await state.fetch_user(user) BotErr.raise_if(await state.is_user_banned(u), f'User {user.mention} is banned in this challenge') BotErr.raise_if(await state.cc.has_started(), f'The challenge has already started.') BotErr.raise_if(await state.has_participant(user), f'User {user.mention} is already participating in this challenge.') await state.cc.add_participant(u.id) await self.db.commit()
async def fetch_title(self, name): t = await self.cc.fetch_title(name) BotErr.raise_if(t is None, f'Title "{name}" does not exist.') return t
async def add_pool(self, ctx, name): state = await State.fetch(self, ctx) BotErr.raise_if(await state.cc.has_pool(name), f'Pool "{name}" already exists.') await state.cc.add_pool(name) await self.db.commit()
async def sync(self, ctx, guild_id=None): state = await State.fetch(self, ctx, allow_started=True, guild_id=guild_id) BotErr.raise_if(state.guild.spreadsheet_key is None, 'Spreadsheet key is not set.') await export(state.guild.spreadsheet_key, state.cc)
async def fetch_pool(self, name): p = await self.cc.fetch_pool(name) BotErr.raise_if(p is None, f'Pool "{name}" does not exist.') return p