async def command(command: str, message: discord.Message): command = command.split(" ") try: command[1] except IndexError: c = message.channel else: try: c = client.get_channel(__common__.stripMentionsToID(command[1])) except: c = message.channel pass try: a = client.get_user(__common__.stripMentionsToID(command[1])) if a is not None: target_user_id = a.id else: target_user_id = None except: target_user_id = None async with message.channel.typing(): if "src=cache" in command: msg_hist = list(client._connection._messages) await message.channel.send(f"[Generating markov chain from {len(client._connection._messages)} messages in cache...]") elif target_user_id is not None: msg_hist = [] for ch in message.guild.text_channels: try: msg_hist.extend(await ch.history(limit=300).flatten()) except: continue msg_hist = [x for x in msg_hist if x.author.id == target_user_id] else: msg_hist = await c.history(limit=3000).flatten() msg_hist_content = [x.clean_content for x in msg_hist if not x.author.bot] src_str = "\n".join(msg_hist_content) model = markovify.NewlineText(src_str) for _ in range(10): ret = model.make_short_sentence(500) if ret is not None: break else: ret = "<Error: For some reason the chain did not generate an acceptable sentence. Please rerun the command.>" await message.channel.send(ret) return
async def command(command: str, message: discord.Message): try: try: u = await client.get_user_info(__common__.stripMentionsToID(command[6:])) except: isUser = False else: isUser = True s = client.get_guild(__common__.stripMentionsToID(command[6:])) if s is None: isServer = False else: isServer = True c = client.get_channel(__common__.stripMentionsToID(command[6:])) if c is None: isTextChannel = False isVoiceChannel = False else: if isinstance(c, discord.TextChannel): isTextChannel = True isVoiceChannel = False elif isinstance(c, discord.VoiceChannel): isTextChannel = False isVoiceChannel = True else: await message.channel.send("Invalid channel type - handling for this is not implemented. Sorry.") return except ValueError: u = message.author isUser = True s = message.guild isServer = True c = message.channel isTextChannel = True isVoiceChannel = False if isUser: user_embed = discord.Embed(title="User Information", description=f"Information about {u.mention}", color=0x3127b3) user_embed = user_embed.set_thumbnail(url=u.avatar_url_as(static_format="png", size=1024)) user_embed = user_embed.add_field(name="Human-Friendly User ID", value=u.name+"#"+u.discriminator) user_embed = user_embed.add_field(name="User Avatar URL", value=u.avatar_url_as(static_format="png", size=1024)) user_embed = user_embed.add_field(name="Raw User ID", value=u.id) user_embed = user_embed.add_field(name="Is Bot", value=u.bot) user_embed = user_embed.add_field(name="Account Creation Time", value=u.created_at) if message.guild.get_member(u.id) is not None: m = message.guild.get_member(u.id) roles = "\n".join([f"{x.name} ({x.id})" for x in m.roles][::-1]) user_embed = user_embed.add_field(name="Roles in this server", value=roles, inline=False) user_embed = user_embed.add_field(name="User Nickname", value=m.display_name) user_embed = user_embed.add_field(name="Joined this server on", value=m.joined_at.__str__()) if isServer: server_embed = discord.Embed(title="Server Information", description=f"Information about the server {s.name}", color=0xb368a2) server_embed = server_embed.set_thumbnail(url=s.icon_url) server_embed = server_embed.add_field(name="Name", value=s.name) server_embed = server_embed.add_field(name="Raw ID", value=s.id) icon = s.icon_url_as(format="png", size=1024) if icon == "": icon = "[no custom icon]" server_embed = server_embed.add_field(name="Icon URL", value=icon) server_embed = server_embed.add_field(name="Owner", value=f"{s.owner.name}#{s.owner.discriminator} ({s.owner.id})") server_embed = server_embed.add_field(name="Creation Time", value=s.created_at) server_embed = server_embed.add_field(name="Member Count", value=s.member_count) server_embed = server_embed.add_field(name="Role Count", value=len(s.roles)) server_embed = server_embed.add_field(name="Emoji Count", value=len(s.emojis)) server_embed = server_embed.add_field(name="Region", value=s.region) server_embed = server_embed.add_field(name="Is offline?", value=s.unavailable) server_embed = server_embed.add_field(name="Admin 2FA required?", value=s.mfa_level) server_embed = server_embed.add_field(name="Verification Level", value=s.verification_level) if isTextChannel: channel_embed = discord.Embed(title="Channel Information", description=f"Information about the text channel {c.mention}", color=0x8f44a2) channel_embed = channel_embed.add_field(name="Name", value=c.name) channel_embed = channel_embed.add_field(name="ID", value=c.id) channel_embed = channel_embed.add_field(name="Server", value=f"{c.guild.name} ({c.guild.id})") t = c.topic if t == "": t = "[none]" channel_embed = channel_embed.add_field(name="Topic", value=t, inline=False) channel_embed = channel_embed.add_field(name="Creation Time", value=c.created_at) if isVoiceChannel: channel_embed = discord.Embed(title="Channel Information", description=f"Information about the voice channel {c.name}", color=0x8f44a2) channel_embed = channel_embed.add_field(name="Name", value=c.name) channel_embed = channel_embed.add_field(name="ID", value=c.id) channel_embed = channel_embed.add_field(name="Server", value=f"{c.guild.name} ({c.guild.id})") channel_embed = channel_embed.add_field(name="Bitrate", value=c.bitrate) channel_embed = channel_embed.add_field(name="User Limit", value=c.user_limit) channel_embed = channel_embed.add_field(name="Creation Time", value=c.created_at) in_vc = "\n".join([f"{x.display_name} ({x.name}#{x.discriminator} - {x.id})" for x in c.members]) if in_vc == "": in_vc = "[none]" channel_embed = channel_embed.add_field(name="Members Currently In Channel", value=in_vc) if isUser: await message.channel.send(embed=user_embed) await asyncio.sleep(0.05) if isServer: await message.channel.send(embed=server_embed) await asyncio.sleep(0.05) if isTextChannel or isVoiceChannel: await message.channel.send(embed=channel_embed) await asyncio.sleep(0.05) if not (isUser or isServer or (isTextChannel or isVoiceChannel)): # if not a user, server, or channel: await message.channel.send(f"Unknown user, server, or channel with ID {command[6:]}. I might not be able to 'see' that object.") return
async def command(parts: str, message: discord.Message): parts = parts.split(" ") response = discord.Embed() # get all optional arguments and check them try: charlimit = parts.pop(parts.index("--charlimit") + 1) parts.pop(parts.index("--charlimit")) except IndexError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: No number provided after --charlimit argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return except ValueError: charlimit = 1500 try: attempts = parts.pop(parts.index("--attempts") + 1) parts.pop(parts.index("--attempts")) except IndexError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: No number provided after --attempts argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return except ValueError: attempts = 25 try: size = parts.pop(parts.index("--size") + 1) parts.pop(parts.index("--size")) except IndexError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: No number provided after --size argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return except ValueError: size = 3 try: charlimit = int(charlimit) except ValueError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: Invalid number passed as --charlimit argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return try: attempts = int(attempts) except ValueError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: Invalid number passed as --attempts argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return try: size = int(size) except ValueError: response.colour = 0xb81209 response.title = "Markov function error" response.description = "Argument error: Invalid number passed as --size argument" response.set_footer(text=datetime.utcnow().__str__()) await message.channel.send(embed=response) return # determine our target. target = None target_type = "unknown" # check for nothing if len(parts) is 1: target = message.channel target_type = "channel" if len(parts) > 1: # get channel if it is one c = client.get_channel(__common__.stripMentionsToID(parts[1])) if c is not None: target = c target_type = "channel" u = client.get_user(__common__.stripMentionsToID(parts[1])) if u is not None: target = u target_type = "user" if target_type == "unknown": response.colour = 0xb81209 response.title = f"Markov function failed" response.description = "Error running markov function: Unknown object specified" response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name}" ) await message.channel.send(embed=response) # run different outputs for different input types if target_type == "user": async with message.channel.typing(): target_member = message.guild.get_member(target.id) if target_member is not None: display_target = f"{target_member.display_name}" else: display_target = f"{target.name}#{target.discriminator}" try: count, result = await get_user_markov(target.id, message, size, charlimit, attempts) except KeyError: await message.channel.send( f"{message.author.mention} The user you have specified has had no recent messages. This function only works on users who have been active recently." ) return if result is None: response.colour = 0xb81209 response.title = "Markov function failed" response.description = "Unable to run markov chain - try running the command again" response.set_author(name=display_target, icon_url=target.avatar_url_as(format="png", size=128)) response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name} | {count} messages in input" ) await message.channel.send(embed=response) return response.colour = 0x243b61 response.description = result response.set_author(name=display_target, icon_url=target.avatar_url_as(format="png", size=128)) response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name} | {count} messages in input" ) await message.channel.send(embed=response) return if target_type == "channel": async with message.channel.typing(): count, result = await get_channel_markov(target, size, charlimit, attempts) if count is None: # permissions error response.colour = 0xb81209 response.title = "Markov chain failed" response.description = "Unable to run markov chain: Insufficient permissions to read channel history" response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name}" ) response.set_author(name=f"#{target.name}") # if we can't read the channel we should check if we can write to it also try: await message.channel.send(embed=response) except discord.Forbidden: pass if result is None: response.colour = 0xb81209 response.title = "Markov function failed" response.description = "Unable to run markov chain - try running the command again" response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name} | {count} messages in input" ) response.set_author(name=f"#{target.name}") await message.channel.send(embed=response) return response.colour = 0x243b61 response.description = result response.set_footer( text= f"{datetime.utcnow().__str__()} | Markov run by @{message.author.display_name} | {count} messages in input" ) response.set_author(name=f"#{target.name}") await message.channel.send(embed=response) return return
async def logstat(command: str, message: discord.Message): global use_mpl if not __common__.check_permission(message.author): await message.add_reaction("❌") return profiling = "--profile" in message.content async with message.channel.typing(): # now we can actually see what our user wants of us # args: logstat 30(days) users 25(top X) # first we need to check if there's enough arguments parts = command.split(" ") # check if they want textual format try: parts.pop(parts.index("--text")) use_mpl = False except ValueError: use_mpl = True if use_mpl else False if len(parts) < 3: await message.channel.send( "Not enough arguments provided to command. See help for help.") return try: limit = datetime.datetime.utcnow() - datetime.timedelta( days=int(parts[1])) except ValueError: await message.channel.send( "First argument to command `logstat` must be number of days behind log to check, as an int" ) return start = time.perf_counter() if "--test" not in message.content: target_guild = message.guild.id logfiles = [x for x in os.listdir("logs/")] # list of all log files all_log_lines = [] # get every single line from those files for f in logfiles: with open("logs/" + f, "r", encoding="utf-8") as file: all_log_lines.extend(file.readlines()) await asyncio.sleep(0.1) else: target_guild = 364480908528451584 logfiles = [x for x in os.listdir("logstat_control_logs/") ] # list of all log files all_log_lines = [] # get every single line from those files for f in logfiles: with open("logs/" + f, "r", encoding="utf-8") as file: all_log_lines.extend(file.readlines()) end = time.perf_counter() if profiling: await message.channel.send( f"profiling: Processing time to load all log lines was {(end-start)*1000:.4f} ms" ) parsed_logs: List[Dict] = [] # we'll now loop through all the lines and parse them into dicts i = 0 if profiling: await message.channel.send(f"{len(all_log_lines)} lines to process" ) start = time.perf_counter() for line in all_log_lines: new_entry = {} try: new_entry["ts"] = datetime.datetime.strptime( line[:29], "[%Y-%m-%d %H:%M:%S.%f] ") except: continue # if there's no timestamp, just move on line_lvl = match_loglevel(line) if line_lvl: new_entry["lvl"] = line_lvl else: continue # all lines we're interested in looking at should have a loglevel on them anyways line_server_id = match_server_id(line) if line_server_id: new_entry["server_id"] = line_server_id else: continue line_channel_id = match_channel_id(line) if line_channel_id: new_entry["channel_id"] = line_channel_id else: continue line_message_id = match_message_id(line) if line_message_id: new_entry["message_id"] = line_message_id else: continue line_user_id = match_user_id(line) if line_user_id: new_entry["user_id"] = line_user_id else: continue # finally, we can add our parsed line into the list parsed_logs.append(new_entry) i += 1 if i % 10000 is 0: await asyncio.sleep(0.01) # i = 0 # split_workload = [] # while True: # if i > 1000: # raise RecursionError("infinite loop ran away, abandoning operation (more than 75 000 000 log lines?!)") # split_workload.append(all_log_lines[(i*75000):((i+1)*75000)]) # if not all_log_lines[(i * 75000):((i + 1) * 75000)]: # break # i += 1 # # pool_event_loop = asyncio.new_event_loop() # with ProcessPoolExecutor() as pool: # results = [await pool_event_loop.run_in_executor(pool, parse_lines, workload) for workload in split_workload] # pool_event_loop.stop() # pool_event_loop.close() # # for x in results: # parsed_logs.extend(x) end = time.perf_counter() if profiling: await message.channel.send( f"profiling: Processing time to parse all log lines: {(end-start)*1000:.4f} ms ({((end-start)*1_000_000)/len(all_log_lines):.3f} us/line)" ) await asyncio.sleep(0.1) # now we'll filter by time and server # list comprehensions are used here because in testing they showed to be about 8x faster than filter() snipped_logs = [x for x in parsed_logs if (x["ts"] > limit)] filtered_logs = [ x for x in snipped_logs if x["server_id"] == target_guild ] if parts[2] in ["users", "user"]: # check if need to filter to a channel try: filter_channel = __common__.stripMentionsToID(parts[4]) except TypeError: # ignore, we're not filtering to a channel pass except IndexError: # ignore, we're not filtering to a channel pass else: filtered_logs = [ x for x in filtered_logs if x["channel_id"] == filter_channel ] if parts[2] in ["channel", "channels"]: # check if we need to filter to a user try: filter_user = __common__.stripMentionsToID(parts[3]) except TypeError: # ignore, we're not filtering to a user pass except IndexError: # ignore, we're not filtering to a user pass else: filtered_logs = [ x for x in filtered_logs if x["user_id"] == filter_user ] record = Counter() if parts[2] in ["users", "user"]: item = "users" try: scoreboard = int(parts[3]) except: # it's fine scoreboard = 25 n = 0 for entry in filtered_logs: record[entry["user_id"]] += 1 n += 1 if n % 5000 is 0: await asyncio.sleep(0.1) top = record.most_common(scoreboard) top_mentions = {await get_human_id(x, message): y for x, y in top} elif parts[2] in ["channel", "channels"]: item = "channels" scoreboard = len(message.guild.text_channels) start = time.perf_counter() for entry in filtered_logs: record[entry["channel_id"]] += 1 end = time.perf_counter() log.debug( f"logstat: Processing time to count all users: {(end-start)*1000:.4f} ms ({((end-start)*1_000_000)/len(filtered_logs):.3f} us/entry)" ) if profiling else None top = record.most_common(scoreboard) top_mentions = { get_channel_name(x, message): y for x, y in top if y is not 0 } elif parts[2] in ["active"]: for entry in filtered_logs: record[entry["user_id"]] += 1 await message.channel.send( f"In the past {int(parts[1])} days, there have been {len(record.most_common())} active unique users." ) return else: await message.channel.send( "Unknown item to get records for. See help for help.") return if not use_mpl: data = "" i = 0 for x, y in top_mentions.items(): i += 1 data += f"`{'0' if i < 100 else ''}{'0' if i < 10 else ''}{str(i)}: {'0' if y < 10000 else ''}{'0' if y < 1000 else ''}{'0' if y < 100 else ''}{'0' if y < 10 else ''}{str(y)} messages:` {x}\n" embed = discord.Embed( title=f"Logfile statistics for past {int(parts[1])} days", description= f"Here are the top {scoreboard} {item} in this server, sorted by number of messages.\nTotal messages: {len(record)}\n" + data) try: await message.channel.send(embed=embed) except: await message.channel.send( "Looks like that information was too long to post, sorry. It's been dumped to the log instead." ) log.info( f"logstat command could not be output back (too many items). here's the data:\n{data}" ) if use_mpl: plot.rcdefaults() plot.rcParams.update({'figure.autolayout': True}) figure, ax = plot.subplots() ax.barh(range(len(top_mentions)), list(top_mentions.values()), align='center') ax.set_xticks(range(len(top_mentions)), list(top_mentions.keys())) ax.set_yticks(range(len(top_mentions))) ax.set_yticklabels(list(top_mentions.keys())) ax.invert_yaxis() plot.show() return
async def command(command: str, message: discord.Message): parts = command.split(" ") if len(parts) == 1: # no following argument await message.channel.send(f"You ran this command without arguments. The old `{client.default_prefix}info` command has been moved to `{client.default_prefix}about`, and this command is now what `{client.default_prefix}debug` used to be.") return try: try: u = await client.fetch_user(__common__.stripMentionsToID(parts[1])) except TypeError: await message.channel.send("Invalid argument: integer ID or mentions are acceptable") return except: isUser = False else: isUser = True s = client.get_guild(__common__.stripMentionsToID(parts[1])) if s is None: isServer = False else: isServer = True c = client.get_channel(__common__.stripMentionsToID(parts[1])) if c is None: isTextChannel = False isVoiceChannel = False else: if isinstance(c, discord.TextChannel): isTextChannel = True isVoiceChannel = False elif isinstance(c, discord.VoiceChannel): isTextChannel = False isVoiceChannel = True else: await message.channel.send("Invalid channel type - handling for this is not implemented. Sorry.") return except ValueError: u = message.author isUser = True s = message.guild isServer = True c = message.channel isTextChannel = True isVoiceChannel = False if isUser and not "--status" in command: user_embed = discord.Embed(title="User Information", description=f"Information about {u.mention}", color=0x3127b3) user_embed = user_embed.set_thumbnail(url=u.avatar_url_as(static_format="png", size=1024)) user_embed = user_embed.add_field(name="Human-Friendly User ID", value=u.name+"#"+u.discriminator) user_embed = user_embed.add_field(name="User Avatar URL", value=u.avatar_url_as(static_format="png", size=1024)) user_embed = user_embed.add_field(name="Raw User ID", value=u.id) user_embed = user_embed.add_field(name="Is Bot", value=u.bot) user_embed = user_embed.add_field(name="Account Creation Time", value=u.created_at) if message.guild.get_member(u.id) is not None: m = message.guild.get_member(u.id) roles = "\n".join([f"{x.name} ({x.id})" for x in m.roles][::-1]) user_embed = user_embed.add_field(name="Roles in this server", value=roles, inline=False) user_embed = user_embed.add_field(name="User Nickname", value=m.display_name) user_embed = user_embed.add_field(name="Joined this server on", value=m.joined_at.__str__()) if get_any_member(u.id) is not None: m = get_any_member(u.id) user_embed = user_embed.add_field(name="Current State", value=f"Apparent Status: {status_emoji(m.status)}\n" f"Desktop: {status_emoji(m.desktop_status)}\n" f"Web: {status_emoji(m.web_status)}\n" f"Mobile: {status_emoji(m.mobile_status)}\n", inline=False) user_embed = user_embed.set_footer(text=datetime.datetime.utcnow().__str__()) if isUser and "--status" in command: if get_any_member(u.id) is not None: m = get_any_member(u.id) else: return await message.channel.send("Cannot get status: I do not share any servers with this user (in order to get a user's status I must share a server with them)") user_embed = discord.Embed(description=f"Apparent Status: {status_emoji(m.status)}\n" f"Desktop: {status_emoji(m.desktop_status)}\n" f"Web: {status_emoji(m.web_status)}\n" f"Mobile: {status_emoji(m.mobile_status)}\n" f"Is on mobile? {m.is_on_mobile()}",) user_embed = user_embed.set_author(icon_url=m.avatar_url_as(format="png", size=128), name=f"{m.name}#{m.discriminator}") user_embed = user_embed.set_footer(text=datetime.datetime.utcnow().__str__()) if isServer: server_embed = discord.Embed(title="Server Information", color=0xb368a2) server_embed = server_embed.add_field(name="Name", value=s.name) server_embed = server_embed.add_field(name="Raw ID", value=s.id) icon = s.icon_url_as(format="png", size=1024) if not icon: # Used to be != "" before icon was a discord.Asset, credit to eyyyyyy/0x5c for finding/fixing this icon = "[no custom icon]" server_embed = server_embed.set_thumbnail(url=s.icon_url) server_embed = server_embed.add_field(name="Icon URL", value=icon) server_embed = server_embed.add_field(name="Owner", value=f"{s.owner.name}#{s.owner.discriminator}\nID {s.owner.id}") server_embed = server_embed.add_field(name="Member Count", value=s.member_count) server_embed = server_embed.add_field(name="Role Count", value=len(s.roles)) server_embed = server_embed.add_field(name="Emoji Count", value=len(s.emojis)) server_embed = server_embed.add_field(name="Creation Time", value=s.created_at) server_embed = server_embed.add_field(name="Region", value=s.region) server_embed = server_embed.add_field(name="Is offline?", value=s.unavailable) server_embed = server_embed.add_field(name="Admin 2FA required?", value=bool(s.mfa_level)) server_embed = server_embed.add_field(name="Verification Level", value=s.verification_level) server_embed = server_embed.set_footer(text=datetime.datetime.utcnow().__str__()) if isTextChannel: channel_embed = discord.Embed(title="Channel Information", description=f"Information about the text channel {c.mention}", color=0x8f44a2) channel_embed = channel_embed.add_field(name="Name", value=c.name) channel_embed = channel_embed.add_field(name="ID", value=c.id) channel_embed = channel_embed.add_field(name="Server", value=f"{c.guild.name} ({c.guild.id})") t = c.topic if t == "": t = "[none]" channel_embed = channel_embed.add_field(name="Topic", value=t, inline=False) channel_embed = channel_embed.add_field(name="Creation Time", value=c.created_at) if isVoiceChannel: channel_embed = discord.Embed(title="Channel Information", description=f"Information about the voice channel {c.name}", color=0x8f44a2) channel_embed = channel_embed.add_field(name="Name", value=c.name) channel_embed = channel_embed.add_field(name="ID", value=c.id) channel_embed = channel_embed.add_field(name="Server", value=f"{c.guild.name} ({c.guild.id})") channel_embed = channel_embed.add_field(name="Bitrate", value=c.bitrate) channel_embed = channel_embed.add_field(name="User Limit", value=c.user_limit) channel_embed = channel_embed.add_field(name="Creation Time", value=c.created_at) in_vc = "\n".join([f"{x.display_name} ({x.name}#{x.discriminator} - {x.id})" for x in c.members]) if in_vc == "": in_vc = "[none]" channel_embed = channel_embed.add_field(name="Members Currently In Channel", value=in_vc) if isUser: await message.channel.send(embed=user_embed) await asyncio.sleep(0.05) if isServer: await message.channel.send(embed=server_embed) await asyncio.sleep(0.05) if isTextChannel or isVoiceChannel: channel_embed = channel_embed.set_footer(text=datetime.datetime.utcnow().__str__()) await message.channel.send(embed=channel_embed) await asyncio.sleep(0.05) if not (isUser or isServer or (isTextChannel or isVoiceChannel)): # if not a user, server, or channel: await message.channel.send(f"Unknown user, server, or channel with ID {parts[1]}. I might not be able to 'see' that object.") return