Example #1
0
async def command(command: str, message: discord.Message):
    if not __common__.check_permission(message.author):
        await message.add_reaction("❌")
        if message.author.id == key.shutdown_easter_egg_user:
            await message.channel.send("*hehehe*\n\nCan't fool me! >:3")
        return
    else:
        parts = command.split(" ")
        try:
            target_pid = int(parts[2])
        except IndexError:
            target_pid = os.getpid()
        except ValueError:
            await message.channel.send("Invalid integer of PID to kill")
            return
        if target_pid == os.getpid():
            # double check we want to
            uptime = time.perf_counter() - client.first_execution
            m = await message.channel.send(
                "Are you sure you want to shut down the bot?\n"
                f"Hostname: {gethostname()}\n"
                f"Uptime: {uptime/86400:.4f} days\n"
                f"To confirm shutdown, react to this message with ☑ in 30 seconds"
            )
            await m.add_reaction("☑")

            def reaction_check(reaction, user):
                return (reaction.message.id == m.id
                        and __common__.check_permission(user)
                        and reaction.emoji == "☑")

            try:
                await client.wait_for("reaction_add",
                                      check=reaction_check,
                                      timeout=30)
            except asyncio.TimeoutError:
                await m.add_reaction("❌")
                await m.remove_reaction("☑", m.guild.me)
                await message.add_reaction("❌")
                await m.edit(content=m.content.replace(
                    "To confirm shutdown, react to this message with ☑ in 30 seconds",
                    "Shutdown timed out. Deleting this message in 30 seconds."
                ),
                             delete_after=30)
                return
            else:
                client.active = False
                await m.delete()
                await message.add_reaction("☑")
                await message.channel.send("Shutting down bot...")
                await message.channel.send(
                    f"Uptime: { time.perf_counter() - client.first_execution:.3f} seconds ({(time.perf_counter() - client.first_execution) / 86400:.3f} days)"
                )
                await asyncio.sleep(
                    0.1)  # give the above a chance to do its thing
                log.info(
                    f"Bot shutdown initiated at {datetime.utcnow().__str__()} by {message.author.name}#{message.author.discriminator}"
                )
                await client.on_shutdown()
    return
Example #2
0
async def command(command: str, message: discord.Message):
	if not __common__.check_permission(message.author):
		await message.add_reaction("❌")
		if message.author.id == key.shutdown_easter_egg_user:
			await message.channel.send("*hehehe*\n\nCan't fool me! >:3")
		return
	else:
		parts = command.split(" ")
		try:
			target_pid = int(parts[2])
		except IndexError:
			target_pid = os.getpid()
		except ValueError:
			await message.channel.send("Invalid integer of PID to kill")
			return
		if target_pid == os.getpid():
			await message.channel.send("Shutting down bot...")
			await message.channel.send(f"Uptime: {time.perf_counter() - client.first_execution:.3f} seconds ({(time.perf_counter() - client.first_execution)/86400:.3f} days)")
			log.info(f"Bot shutdown initiated at {datetime.utcnow().__str__()} by {message.author.name}#{message.author.discriminator}")
			await client.on_shutdown()
	return
Example #3
0
async def logstat(command: str, message: discord.Message):
    await message.channel.send(
        "Command disabled: System will run out of memory if ran")
    return
    global use_mpl
    if not __common__.check_permission(message.author):
        await __common__.failure(message)
        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.05)
        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__.strip_to_id(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__.strip_to_id(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
Example #4
0
async def logstat(command: str, message: discord.Message):
    global use_mpl
    if not __common__.check_permission(message.author):
        await message.add_reaction("❌")
        return

    async with message.channel.typing():

        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)

        parsed_logs: List[Dict] = []

        # we'll now loop through all the lines and parse them into dicts
        n = 0
        for line in all_log_lines:
            new_entry = {}

            line_ts = match_date(line)
            if line_ts:
                new_entry["ts"] = line_ts
            else:
                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

            n += 1
            if n % 5000 is 0:
                await asyncio.sleep(0.1)
            # finally, we can add our parsed line into the list
            parsed_logs.append(new_entry)
        # if you've got the above loop collapsed: all lines have been parsed into Dicts with the following properties:
        # ts (datetime), lvl (1char str), server_id, channel_id, message_id, and user_id

        # 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"))
        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

        await asyncio.sleep(0.1)
        # now we'll trim to only the most recent days
        snipped_logs = [x for x in parsed_logs if (x["ts"] > limit)]
        # and then to what's in this server
        await asyncio.sleep(0.1)
        filtered_logs = [
            x for x in snipped_logs if x["server_id"] == message.guild.id
        ]

        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)
            for entry in filtered_logs:
                record[entry["channel_id"]] += 1

            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 use_mpl:
            data = ""
            i = 0
            for x, y in top_mentions:
                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.\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 not 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
Example #5
0
async def emergency_suicide(command: str, message: discord.Message):
	if not __common__.check_permission(message.author):
		await message.add_reaction("❌")
		return
	else:
		os._exit(2)
Example #6
0
 def reaction_check(reaction, user):
     return (reaction.message.id == m.id
             and __common__.check_permission(user)
             and reaction.emoji == "☑")