async def list_active_users(self, ctx, *users: discord.Member): """List only users that are active (or those specified).""" game = get_game(ctx) description = '' users = list(users) if not users: for user_id in game.player_last_seen: try: user = ctx.guild.get_member(int(user_id)) if self.is_active(ctx, user): users.append(user) except: pass users = sorted( sorted(users, key=member_sort_key(ctx)), key=lambda member: self.last_seen_diff(ctx, member) or 1e9) for user in users: last_seen = self.last_seen_diff(ctx, user) if last_seen is None: last_seen_text = "never" elif last_seen < 2: last_seen_text = "very recently" else: last_seen_text = f"about {format_hour_interval(self.last_seen_diff(ctx, user))} ago" description += f"{user.mention} was last seen **{last_seen_text}**" if self.is_active(ctx, user): description += " _(active)_" else: description += " _(inactive)_" description += "\n" await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title=f"Active users ({len(users)})", description=description))
async def private_room(self, ctx: commands.Context, member: Member, *members: Member): msg = await ctx.send(f'{ctx.author.mention} 개인방을 만들고 있습니다...') try: overwrites = { ctx.guild.default_role: PermissionOverwrite(read_messages=False), self.client.user: PermissionOverwrite(read_messages=True) } members = list(members) members.append(member) for member in members: overwrites[member] = PermissionOverwrite(read_messages=True) text_channel: TextChannel = await ctx.guild.create_text_channel( f'개인방 {self.object_id}호실', overwrites=overwrites) voice_channel: VoiceChannel = await ctx.guild.create_voice_channel( f'개인방 {self.object_id}호실', overwrites=overwrites) text_url = f'https://discordapp.com/channels/{ctx.guild.id}/{text_channel.id}' voice_url = f'https://discordapp.com/channels/{ctx.guild.id}/{voice_channel.id}' embed = Embed(title=':spy: 개인방 개설', colour=Colour.blurple(), description='개인방을 개설했습니다.') embed.set_author(name=ctx.author, icon_url=ctx.author.avatar_url) embed.set_thumbnail(url=ctx.guild.icon_url) embed.add_field( name=':busts_in_silhouette: 멤버', inline=False, value= f':small_orange_diamond: {ctx.author}\n:small_blue_diamond: ' + ('\n:small_blue_diamond: '.join( str(member) for member in members))) embed.add_field(name=':envelope: 텍스트 채널', value=f'텍스트 채널을 보려면 **[여기]({text_url})**를 클릭하세요.') embed.add_field( name=':loud_sound: 음성 채널 (화면공유)', value=f'음성 채널에서 화면공유를 활성화하려면 **[여기]({voice_url})**를 클릭하세요.') await msg.edit(content=f'{ctx.author.mention} 개인방을 만들었습니다.', embed=embed) await text_channel.send(embed=embed) self.object_id += 1 except BaseException as e: await msg.edit(content=f'개인방을 만들 수 없습니다...```\n{e}\n```')
async def create(ctx, *to_invit: discord.Member): """ créer une partie de jeu et gérer le jeu. """ # Commande à executer uniquement sur un serveur, pas de messages privés if ctx.guild is None: raise commands.NoPrivateMessage() # l'invitation s'étend sur tout le serveur si les participants ne sont pas initialisés to_invit = list(to_invit) if not to_invit: async for client in ctx.guild.fetch_members(limit=None): to_invit.append(client) to_invit = tuple(to_invit) ### MESSAGE D'INVITATION ### invit_message = f"{ctx.author.name} vous invite pour une partie à Gravenviille.\n {module.emoji_yes} pour accepter\n {module.emoji_no} pour refuser\n {module.emoji_close} pour stoper l'ajout de participant (uniquement {ctx.author.name}#{ctx.author.discriminator})" # ============================ # embed = discord.Embed(title="Invitation", color=0x9e9210) embed.add_field(name="\uFEFF", value=invit_message, inline=False) message = await ctx.send(embed=embed) await message.add_reaction(module.emoji_yes) await message.add_reaction(module.emoji_no) await message.add_reaction(module.emoji_close) # ========================== # ### GET PLAYERS ### # ===variables=== # looking_for_react = True # Espérer une nouvelle reaction Timeout = module.timedelta(seconds=30) # le temps total d'attente start = module.delta_time(*(module.current_time())) # heures de debut player_list = {} # joueur : rôle # -------------- # def check(reaction, user): """booleen de recherche de reaction""" return ((str(reaction.emoji) == module.emoji_yes) or (str(reaction.emoji) == module.emoji_no) or str( reaction.emoji) == module.emoji_close) # -------------- # while looking_for_react: # timeout, la partie seconds timeout = str(module.get_timeout(start))[-2::] try: reaction, reactor = await bot.wait_for('reaction_add', timeout=float(timeout), check=check) except asyncio.TimeoutError: looking_for_react = False else: if (reaction.message.id == message.id and reactor in to_invit): if reactor.guild_permissions.administrator: await ctx.send( f"Désolé {reactor.name}, les membres avec la permission administrateur activée ne peuvent pas jouer car leur rôle permet de voir les salons privés. {module.emoji_sad}") elif reaction.emoji == module.emoji_yes: if reactor not in player_list: player_list[reactor] = "" elif reaction.emoji == module.emoji_no: if reactor in player_list: player_list.pop(reactor) elif (reaction.emoji == module.emoji_close and reactor.id == ctx.author.id): looking_for_react = False players = list(player_list.keys()) # --------------------- # if len(players) < min_player: return await ctx.send( f"Il faut au minimum {min_player} joueurs. Reviens une prochaine fois avec plus de joueur") # message de rôle à envoyer en DM await ctx.send(f"Liste des participants : {', '.join(map(str, [player.name for player in player_list.keys()]))}") ### ASSIGNER DES RÔLES ### # ======variables====== # begin_limit = 0 min_player = 4 limit = floor(2 * len(players[::]) / 3) shuffle(players) role_message = lambda player, role, server, invite: f"{player.name}, vous avez obtenu le rôle {role}\n$lginfo {role} pour plus d'information.\n\nDans 20 secondes les votes commancent sur le serveur {ctx.guild.name}.Lien : {invite}"
async def challenge(self, ctx, *users: discord.Member): users = list(set(users)) if len(users) == 0: await ctx.send(f"The correct usage is `.round challenge @user1 @user2...`") return if ctx.author not in users: users.append(ctx.author) if len(users) > 5: await ctx.send(f"{ctx.author.mention} you can't compete with more than 4 users at a time") return for i in users: if not self.db.handle_in_db(ctx.guild.id, i.id): await ctx.send(f"Handle for {i.name} not set! Use `.handle identify` to register") return if self.db.in_a_round(ctx.guild.id, i.id): await ctx.send(f"{i.name} is already in a round!") return embed = discord.Embed(description=f"{' '.join(x.mention for x in users)} react on the message with ✅ within 30 seconds to join the round. {'Since you are the only participant, this will be a practice round and there will be no rating changes' if len(users) == 1 else ''}", color=discord.Color.purple()) message = await ctx.send(embed=embed) await message.add_reaction("✅") # await asyncio.sleep(30) try: query_time = time.time() all_users_reacted = False while (time.time() - query_time < 30): await asyncio.sleep(2) message = await ctx.channel.fetch_message(message.id) reaction = None for x in message.reactions: if x.emoji == "✅": reaction = x reacted = await reaction.users().flatten() user_cnt = 0 for user in users: if user in reacted user_cnt++; if (user_cnt == len(users)) all_users_reacted = True if(all_users_reacted == False) await ctx.send(f"Unable to start round, {i.name} did not react in time!") return problem_cnt = await get_time_response(self.client, ctx, f"{ctx.author.mention} enter the number of problems between [1, 6]", 30, ctx.author, [1, 6]) if not problem_cnt[0]: await ctx.send(f"{ctx.author.mention} you took too long to decide") return problem_cnt = problem_cnt[1] time = await get_time_response(self.client, ctx, f"{ctx.author.mention} enter the duration of match in minutes between [5, 180]", 30, ctx.author, [5, 180]) if not time[0]: await ctx.send(f"{ctx.author.mention} you took too long to decide") return time = time[1] rating = await get_seq_response(self.client, ctx, f"{ctx.author.mention} enter {problem_cnt} space seperated integers denoting the ratings of problems (between 700 and 4000)", 60, problem_cnt, ctx.author, [700, 4000]) if not rating[0]: await ctx.send(f"{ctx.author.mention} you took too long to decide") return rating = rating[1] points = await get_seq_response(self.client, ctx, f"{ctx.author.mention} enter {problem_cnt} space seperated integer denoting the points of problems (between 100 and 10,000)", 60, problem_cnt, ctx.author, [100, 10000]) if not points[0]: await ctx.send(f"{ctx.author.mention} you took too long to decide") return points = points[1] repeat = await get_time_response(self.client, ctx, f"{ctx.author.mention} do you want a new problem to appear when someone solves a problem (type 1 for yes and 0 for no)", 30, ctx.author, [0, 1]) if not repeat[0]: await ctx.send(f"{ctx.author.mention} you took too long to decide") return repeat = repeat[1] for i in users: if self.db.in_a_round(ctx.guild.id, i.id): await ctx.send(f"{i.name} is already in a round!") return check = False start_time = get_time() First = True handles = [self.db.get_handle(ctx.guild.id, user.id) for user in users] while get_time() - start_time < 60: if First: alts = await self.get_alt_response(self.client, ctx, f"{ctx.author.mention} add alts of users\ntype None if not applicable \nFormat \"Alts: handle_1 handle_2 .. \n\"len(handles) <= 2*len(users) must be satisfied", 60, ctx.author) First = False else: alts = await self.get_alt_response(self.client, ctx, f"{ctx.author.mention} add alts of users", 60, ctx.author) if alts[0]: alts = alts[1] if len(alts) < 1: continue if len(alts) == 1: if alts[0].lower() == "none": check = True alts = [] break else: alts = alts[1:] check = True for alt in alts: res = await self.api.check_handle(alt) if not res[0]: await ctx.send(f"{ctx.author.mention} " + alt + " is not valid codeforces handle, try again") check = False break alts.extend(handles) alts = list(set(alts)) if len(alts) > 2*len(users): await ctx.send(f"{ctx.author.mention} len(handles) <= 2*len(users) must be satisfied") check = False if check: handles = alts break if not check: await ctx.send(f"{ctx.author.mention} you took too long to decide") return await ctx.send(embed=discord.Embed(description="Starting the round...", color=discord.Color.green())) problems = await self.get_user_problems(handles) if not problems[0]: await ctx.send(f"Codeforces API Error!") return solved_problems = problems[1] tot_problems = self.db.get_problems() chosen = [] for i in range(len(rating)): x = rating[i] solved_problems.extend(chosen) problem = self.get_unsolved_problem(solved_problems, tot_problems, handles, x) if not problem: await ctx.send(f"Not enough problems of rating {x} left") return chosen.append(problem) embed = discord.Embed(color=discord.Color.magenta()) embed.set_author(name="Problems") embed.add_field(name="Points", value='\n'.join(str(pt) for pt in points), inline=True) embed.add_field(name="Problem Name", value='\n'.join([f"[{pr[2]}](https://codeforces.com/problemset/problem/{pr[0]}/{pr[1]})" for pr in chosen]), inline=True) embed.add_field(name="Rating", value='\n'.join([str(rt) for rt in rating]), inline=True) embed.set_footer(text=f"Time left: {time} minutes 0 seconds") await ctx.send(embed=embed) self.db.add_to_ongoing_round(ctx, users, rating, points, chosen, time, repeat ,handles) @round.command(name="ongoing", brief="View ongoing rounds") async def ongoing(self, ctx): data = self.db.get_ongoing_rounds(ctx.guild.id) embed = [] for x in data: try: users = [ctx.guild.get_member(int(x1)) for x1 in x[1].split()] status = [int(x1) for x1 in x[7].split()] timestamp = [int(x1) for x1 in x[10].split()] embed_ = self.db.print_round_score(users, status, timestamp, ctx.guild.id, 0) embed_.add_field(name="Time left", value=timeez(x[4]+60*x[8]-int(time.time())), inline=False) embed.append(embed_) except Exception as e: pass if len(embed) == 0: await ctx.send("There are no ongoing rounds") return for i in range(len(embed)): embed[i].set_footer(text=f"Page {i+1} of {len(embed)}") currPage = 0 totPage = len(embed) message = await ctx.send(embed=embed[currPage]) await message.add_reaction("\U000025c0") await message.add_reaction("\U000025b6") def check(reaction, user): return reaction.message.id == message.id and reaction.emoji in ["\U000025c0", "\U000025b6"] and user != self.client.user while True: try: reaction, user = await self.client.wait_for('reaction_add', timeout=180, check=check) try: await reaction.remove(user) except Exception: pass if reaction.emoji == "\U000025c0": currPage = (currPage - 1 + totPage) % totPage await message.edit(embed=embed[currPage]) else: currPage = (currPage + 1 + totPage) % totPage await message.edit(embed=embed[currPage]) except asyncio.TimeoutError: break @round.command(brief="Invalidate a round (Admin Only)") @commands.has_any_role('Admin', 'Moderator') async def _invalidate(self, ctx, member: discord.Member): if not self.db.in_a_round(ctx.guild.id, member.id): await ctx.send(f"{member.name} is not in a round") return self.db.delete_round(ctx.guild.id, member.id) await ctx.send(f"Round deleted") @round.command(name="recent", brief="Show recent rounds") async def recent(self, ctx, user: discord.Member=None): data = self.db.get_recent_rounds(ctx.guild.id, str(user.id) if user else "") content = [] embeds = [] for x in data: try: content.append(self.make_result_embed([ctx.guild.get_member(int(i)) for i in x[1].split()], [int(i) for i in x[7].split()], [int(i) for i in x[10].split()], x[2], x[3], x[11] - x[4])) except Exception as e: print(e) if len(content) == 0: await ctx.send(f"No recent rounds") return currPage = 0 perPage = 5 totPage = math.ceil(len(content) / perPage) for i in range(totPage): embed = discord.Embed(description='\n'.join(content[i*perPage:min((i+1)*perPage, len(content))]), color=discord.Color.purple()) embed.set_author(name="Recent Rounds") embed.set_footer(text=f"Page {i+1} of {totPage}") embeds.append(embed) message = await ctx.send(embed=embeds[currPage]) await message.add_reaction("\U000025c0") await message.add_reaction("\U000025b6") def check(reaction, user): return reaction.message.id == message.id and reaction.emoji in ["\U000025c0", "\U000025b6"] and user != self.client.user while True: try: reaction, user = await self.client.wait_for('reaction_add', timeout=180, check=check) try: await reaction.remove(user) except Exception: pass if reaction.emoji == "\U000025c0": currPage = (currPage - 1 + totPage) % totPage await message.edit(embed=embeds[currPage]) else: currPage = (currPage + 1 + totPage) % totPage await message.edit(embed=embeds[currPage]) except asyncio.TimeoutError: break @round.command(name="invalidate", brief="Invalidate your round") async def invalidate(self, ctx): if not self.db.in_a_round(ctx.guild.id, ctx.author.id): await ctx.send(f"{ctx.author.mention} you are not in a round") return data = self.db.get_round_info(ctx.guild.id, ctx.author.id) try: users = [ctx.guild.get_member(int(x)) for x in data[1].split()] except Exception: await ctx.send(f"{ctx.author.mention} some error occurred! Maybe one of the participants left the server") return msg = await ctx.send(f"{' '.join([x.mention for x in users])} react within 30 seconds to invalidate the match") await msg.add_reaction("✅") await asyncio.sleep(30) message = await ctx.channel.fetch_message(msg.id) reaction = None for x in message.reactions: if x.emoji == "✅": reaction = x reacted = await reaction.users().flatten() for i in users: if i not in reacted: await ctx.send(f"Unable to invalidate round, {i.name} did not react in time!") return self.db.delete_round(ctx.guild.id, ctx.author.id) await ctx.send(f"Match has been invalidated") @round.command(brief="Update matches status for the server") @cooldown(1, 60, BucketType.guild) async def update(self, ctx): await ctx.send(embed=discord.Embed(description="Updating rounds for this server", color=discord.Color.green())) await self.db.update_rounds(self.client, ctx.guild.id) @update.error async def update_error(self, ctx, exc): if isinstance(exc, CommandOnCooldown): await ctx.send(embed=discord.Embed( description=f"Slow down!\nThe cooldown of command is **60s**, pls retry after **{exc.retry_after:,.2f}s**", color=discord.Color.red())) @round.command(name="problems", brief="View problems of a round") async def problems(self, ctx, member: discord.Member=None): if not member: member = ctx.author if not self.db.in_a_round(ctx.guild.id, member.id): await ctx.send(f"{member.name} is not in a round") return x = self.db.get_round_info(ctx.guild.id, member.id) problems = x[6].split() duration = x[8] start = x[4] repeat = x[9] pname = [] for prob in problems: if prob == '0': pname.append( 'No unsolved problems of this rating left' if repeat == 1 else "This problem has been solved") else: id = prob.split('/')[0] idx = prob.split('/')[1] pname.append(f"[{self.db.get_problem_name(id, idx)}](https://codeforces.com/problemset/problem/{prob})") embed = discord.Embed(color=discord.Color.magenta()) embed.set_author(name=f"Problems left") embed.add_field(name="Points", value='\n'.join(x[3].split()), inline=True) embed.add_field(name="Problem", value='\n'.join(pname), inline=True) embed.add_field(name="Rating", value='\n'.join(x[2].split()), inline=True) embed.set_footer(text=f"Time left: {timeez(start + 60 * duration - int(time.time()))}") await ctx.send(embed=embed)
async def make_response(self, ctx, *users: discord.Member): users: List[discord.Member] = list(users) if not user_accepts(ctx.author, name, "thing"): return await ctx.send(f"But you don't like that") if ctx.message.reference is not None: referenced: discord.Message referenced = (ctx.message.reference.cached_message or await ctx.message.channel.fetch_message( ctx.message.reference.message_id)) users.append(referenced.author) allowed = [] role_denied = [] condition_denied = [] for user in users: # noinspection PyTypeChecker if user_accepts(user, name, "thing"): if condition_predicate(ctx.author, user): allowed.append(user) else: condition_denied.append(user) else: role_denied.append(user) if not users: return await ctx.send( "No users", allowed_mentions=discord.AllowedMentions.none()) title_baking = [] if allowed: title_baking.extend(normal) description_baking = [] if allowed and (role_denied or condition_denied): description_baking.append("however") if role_denied: description_baking.extend(reject) if condition_denied: description_baking.extend(condition_rejected) with PhraseBuilder() as builder: title = await builder.build(title_baking, speaker=ctx.bot.user, deferred={ "action_author": ctx.author, "valid": allowed, "rejected": role_denied, "condition": condition_denied }) if title == "": title = discord.Embed.Empty elif len(title) > 256: return await ctx.message.reply( embed=discord.Embed(title="(ಠ_ಠ)"), allowed_mentions=discord.AllowedMentions.none()) description = await builder.build(description_baking, speaker=ctx.bot.user, deferred={ "action_author": ctx.author, "valid": allowed, "rejected": role_denied, "condition": condition_denied }) if description == "": description = discord.Embed.Empty embed = discord.Embed(title=title, description=description) return await ctx.send(embed=embed, allowed_mentions=discord.AllowedMentions.none())
async def challenge(self, ctx, *users: discord.Member): users = list(set(users)) if len(users) == 0: await discord_.send_message( ctx, f"The correct usage is `.round challenge @user1 @user2...`") return if ctx.author not in users: users.append(ctx.author) if len(users) > MAX_ROUND_USERS: await ctx.send( f"{ctx.author.mention} atmost {MAX_ROUND_USERS} users can compete at a time" ) return for i in users: if not self.db.get_handle(ctx.guild.id, i.id): await discord_.send_message( ctx, f"Handle for {i.mention} not set! Use `.handle identify` to register" ) return if self.db.in_a_round(ctx.guild.id, i.id): await discord_.send_message( ctx, f"{i.mention} is already in a round!") return embed = discord.Embed( description= f"{' '.join(x.mention for x in users)} react on the message with ✅ within 30 seconds to join the round. {'Since you are the only participant, this will be a practice round and there will be no rating changes' if len(users) == 1 else ''}", color=discord.Color.purple()) message = await ctx.send(embed=embed) await message.add_reaction("✅") all_reacted = False reacted = [] def check(reaction, user): return reaction.message.id == message.id and reaction.emoji == "✅" and user in users while True: try: reaction, user = await self.client.wait_for('reaction_add', timeout=30, check=check) reacted.append(user) if all(item in reacted for item in users): all_reacted = True break except asyncio.TimeoutError: break if not all_reacted: await discord_.send_message( ctx, f"Unable to start round, some participant(s) did not react in time!" ) return problem_cnt = await discord_.get_time_response( self.client, ctx, f"{ctx.author.mention} enter the number of problems between [1, {MAX_PROBLEMS}]", 30, ctx.author, [1, MAX_PROBLEMS]) if not problem_cnt[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return problem_cnt = problem_cnt[1] duration = await discord_.get_time_response( self.client, ctx, f"{ctx.author.mention} enter the duration of match in minutes between {MATCH_DURATION}", 30, ctx.author, MATCH_DURATION) if not duration[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return duration = duration[1] rating = await discord_.get_seq_response( self.client, ctx, f"{ctx.author.mention} enter {problem_cnt} space seperated integers denoting the ratings of problems (between {LOWER_RATING} and {UPPER_RATING})", 60, problem_cnt, ctx.author, [LOWER_RATING, UPPER_RATING]) if not rating[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return rating = rating[1] points = await discord_.get_seq_response( self.client, ctx, f"{ctx.author.mention} enter {problem_cnt} space seperated integer denoting the points of problems (between 100 and 10,000)", 60, problem_cnt, ctx.author, [100, 10000]) if not points[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return points = points[1] repeat = await discord_.get_time_response( self.client, ctx, f"{ctx.author.mention} do you want a new problem to appear when someone solves a problem (type 1 for yes and 0 for no)", 30, ctx.author, [0, 1]) if not repeat[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return repeat = repeat[1] for i in users: if self.db.in_a_round(ctx.guild.id, i.id): await discord_.send_message( ctx, f"{i.name} is already in a round!") return alts = await discord_.get_alt_response( self.client, ctx, f"{ctx.author.mention} Do you want to add any alts? Type none if not applicable else type `alts: handle_1 handle_2 ...` You can add upto **{MAX_ALTS}** alt(s)", MAX_ALTS, 60, ctx.author) if not alts: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return alts = alts[1] tournament = 0 if len(users) == 2 and (await tournament_helper.is_a_match( ctx.guild.id, users[0].id, users[1].id, self.api, self.db)): tournament = await discord_.get_time_response( self.client, ctx, f"{ctx.author.mention} this round is a part of the tournament. Do you want the result of this round to be counted in the tournament. Type `1` for yes and `0` for no", 30, ctx.author, [0, 1]) if not tournament[0]: await discord_.send_message( ctx, f"{ctx.author.mention} you took too long to decide") return tournament = tournament[1] await ctx.send(embed=discord.Embed(description="Starting the round...", color=discord.Color.green())) problems = await codeforces.find_problems( [self.db.get_handle(ctx.guild.id, x.id) for x in users] + alts, rating) if not problems[0]: await discord_.send_message(ctx, problems[1]) return problems = problems[1] self.db.add_to_ongoing_round(ctx, users, rating, points, problems, duration, repeat, alts, tournament) round_info = self.db.get_round_info(ctx.guild.id, users[0].id) await ctx.send(embed=discord_.round_problems_embed(round_info))