Compare commits

..

4 commits

Author SHA1 Message Date
TSRBerry 37651883bc
Fix broken build id regex & Return a set from get_filepaths (#110)
* log_analyser: Match application or title when searching for build IDs

* log_analyser: Return a set from get_filepaths
2024-09-24 20:09:33 +02:00
TSR Berry fcde1f365c
Update flake.lock 2024-09-01 15:54:58 +02:00
TSRBerry 147011eba1
Automatically block analysis of logs containing blocked contents in paths (#108)
* Extract paths from logs and check for blocked content

* Extract paths in command line analyzer

* Split disabled paths message if necessary

* Log the blocked path that caused a warning

* Remove duplicate command alias

* Remove bad characters from extracted filepaths

* Fix is_path_disabled() only checking the full path

* Apply formatting

* Improve wording for the warning embeds

* Apply formatting
2024-09-01 15:53:44 +02:00
ekuland 76fe1dbbd4
Add Note for Rosetta (#109)
* Add get_cpu_notes

Check cpu for Virtual Apple and Note to disable Rosetta

* Update ryujinx_log_analyser.py

fix misspelling

* Apply formatting
2024-08-27 19:34:24 +02:00
6 changed files with 191 additions and 20 deletions

View file

@ -59,11 +59,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1715668745,
"narHash": "sha256-xp62OkRkbUDNUc6VSqH02jB0FbOS+MsfMb7wL1RJOfA=",
"lastModified": 1720535198,
"narHash": "sha256-zwVvxrdIzralnSbcpghA92tWu2DV2lwv89xZc8MTrbg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ddcaffecdf098822d944d4147dd8da30b4e6843",
"rev": "205fd4226592cc83fd4c0885a3e4c9c400efabb5",
"type": "github"
},
"original": {
@ -84,11 +84,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1715251496,
"narHash": "sha256-vRBfJCKvJtu5sYev56XStirA3lAOPv0EkoEV2Nfc+tc=",
"lastModified": 1724417163,
"narHash": "sha256-gD0N0pnKxWJcKtbetlkKOIumS0Zovgxx/nMfOIJIzoI=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "291a863e866972f356967d0a270b259f46bf987f",
"rev": "7619e43c2b48c29e24b88a415256f09df96ec276",
"type": "github"
},
"original": {
@ -156,11 +156,11 @@
]
},
"locked": {
"lastModified": 1714058656,
"narHash": "sha256-Qv4RBm4LKuO4fNOfx9wl40W2rBbv5u5m+whxRYUMiaA=",
"lastModified": 1719749022,
"narHash": "sha256-ddPKHcqaKCIFSFc/cvxS14goUhCOAwsM1PbMr0ZtHMg=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "c6aaf729f34a36c445618580a9f95a48f5e4e03f",
"rev": "8df5ff62195d4e67e2264df0b7f5e8c9995fd0bd",
"type": "github"
},
"original": {

14
poetry.lock generated
View file

@ -248,22 +248,22 @@ test-randomorder = ["pytest-randomly"]
[[package]]
name = "discord-py"
version = "2.4.0"
version = "2.3.2"
description = "A Python wrapper for the Discord API"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.8.0"
files = [
{file = "discord.py-2.4.0-py3-none-any.whl", hash = "sha256:b8af6711c70f7e62160bfbecb55be699b5cb69d007426759ab8ab06b1bd77d1d"},
{file = "discord_py-2.4.0.tar.gz", hash = "sha256:d07cb2a223a185873a1d0ee78b9faa9597e45b3f6186df21a95cec1e9bcdc9a5"},
{file = "discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6"},
{file = "discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c"},
]
[package.dependencies]
aiohttp = ">=3.7.4,<4"
[package.extras]
docs = ["sphinx (==4.4.0)", "sphinx-inline-tabs (==2023.4.21)", "sphinxcontrib-applehelp (==1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (==2.0.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport (==1.2.4)", "typing-extensions (>=4.3,<5)"]
docs = ["sphinx (==4.4.0)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport", "typing-extensions (>=4.3,<5)"]
speed = ["Brotli", "aiodns (>=1.1)", "cchardet (==2.1.7)", "orjson (>=3.5.4)"]
test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)", "tzdata"]
test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)"]
voice = ["PyNaCl (>=1.3.0,<1.6)"]
[[package]]
@ -683,4 +683,4 @@ multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "a69a433f44d37a63825a39563d8bf3fedea9a3c64c1a4214f54c86b1f3ddaff4"
content-hash = "1453fcd147cbf31437ecaa4b37deb0f5d880579a2b93b5f668c28c154ed299d8"

View file

@ -14,7 +14,7 @@ humanize = "^4.8.0"
parsedatetime = "^2.6"
aiohttp = "^3.9.3"
gidgethub = "^5.3.0"
"discord.py" = "^2.4.0"
"discord.py" = "^2.3.2"
[tool.poetry.dev-dependencies]

View file

@ -1,5 +1,6 @@
import logging
import re
from typing import Optional
import aiohttp
from discord import Colour, Embed, Message, Attachment
@ -23,6 +24,12 @@ from robocop_ng.helpers.disabled_ids import (
remove_disabled_ro_section,
remove_disable_id,
)
from robocop_ng.helpers.disabled_paths import (
is_path_disabled,
get_disabled_paths,
add_disabled_path,
remove_disabled_path,
)
from robocop_ng.helpers.ryujinx_log_analyser import LogAnalyser
logging.basicConfig(
@ -92,6 +99,15 @@ class LogFileReader(Cog):
return True
return is_ro_section_disabled(self.bot, main_ro_section)
def contains_blocked_paths(self, log_file: str) -> Optional[str]:
filepaths = LogAnalyser.get_filepaths(log_file)
if filepaths is None:
return None
for filepath in filepaths:
if is_path_disabled(self.bot, filepath):
return filepath
return None
async def blocked_game_action(self, message: Message) -> Embed:
warn_command = self.bot.get_command("warn")
if warn_command is not None:
@ -106,7 +122,7 @@ class LogFileReader(Cog):
)
else:
logging.error(
f"Couldn't find 'warn' command. Unable to warn {message.author}."
f"Couldn't find 'warn' command. Unable to warn {message.author} for uploading a log of a blocked game."
)
pirate_role = message.guild.get_role(self.bot.config.named_roles["pirate"])
@ -116,7 +132,38 @@ class LogFileReader(Cog):
title="⛔ Blocked game detected ⛔",
colour=Colour(0xFF0000),
description="This log contains a blocked game and has been removed.\n"
"The user has been warned and the pirate role was applied.",
"The user has been warned and the pirate role has been applied.",
)
embed.set_footer(text=f"Log uploaded by @{message.author.name}")
await message.delete()
return embed
async def blocked_path_action(self, message: Message, blocked_path: str) -> Embed:
warn_command = self.bot.get_command("warn")
if warn_command is not None:
warn_message = await message.reply(
".warn This log contains blocked content in paths."
)
warn_context = await self.bot.get_context(warn_message)
await warn_context.invoke(
warn_command,
target=None,
reason=f"This log contains blocked content in paths: '{blocked_path}'",
)
else:
logging.error(
f"Couldn't find 'warn' command. Unable to warn {message.author} for uploading a log "
f"containing a blocked content in paths."
)
pirate_role = message.guild.get_role(self.bot.config.named_roles["pirate"])
await message.author.add_roles(pirate_role)
embed = Embed(
title="⛔ Blocked content in path detected ⛔",
colour=Colour(0xFF0000),
description="This log contains paths containing blocked content and has been removed.\n"
"The user has been warned and the pirate role has been applied.",
)
embed.set_footer(text=f"Log uploaded by @{message.author.name}")
await message.delete()
@ -243,6 +290,9 @@ class LogFileReader(Cog):
if self.is_game_blocked(log_file):
return await self.blocked_game_action(message)
blocked_path = self.contains_blocked_paths(log_file)
if blocked_path:
return await self.blocked_path_action(message, blocked_path)
for role in message.author.roles:
if role.id in self.disallowed_roles:
@ -449,6 +499,61 @@ class LogFileReader(Cog):
else:
return await ctx.send(f"No read-only section blocked for '{disable_id}'.")
@commands.check(check_if_staff)
@commands.command(
aliases=["disallow_path", "forbid_path", "block_path", "blockpath"]
)
async def disable_path(self, ctx: Context, block_path: str):
if add_disabled_path(self.bot, block_path):
return await ctx.send(f"Path content `{block_path}` is now blocked!")
else:
return await ctx.send(f"Path content `{block_path}` is already blocked.")
@commands.check(check_if_staff)
@commands.command(
aliases=[
"allow_path",
"unblock_path",
"unblockpath",
]
)
async def enable_path(self, ctx: Context, block_path: str):
if remove_disabled_path(self.bot, block_path):
return await ctx.send(f"Path content `{block_path}` is now unblocked!")
else:
return await ctx.send(f"No blocked path content '{block_path}' found.")
@commands.check(check_if_staff)
@commands.command(
aliases=[
"disabled_paths",
"blocked_paths",
"listdisabledpaths",
"listblockedpaths",
"list_blocked_paths",
]
)
async def list_disabled_paths(self, ctx: Context):
messages = []
disabled_paths = get_disabled_paths(self.bot)
message = (
"**Blocking analysis of logs containing the following content in paths:**\n"
)
for entry in disabled_paths:
if len(message) >= 1500:
messages.append(message)
message = f"- `{entry}`\n"
else:
message += f"- `{entry}`\n"
if message not in messages:
# Add the last message as well
messages.append(message)
for msg in messages:
await ctx.send(msg)
async def analyse_log_message(self, message: Message, attachment_index=0):
author_id = message.author.id
author_mention = message.author.mention
@ -563,6 +668,12 @@ class LogFileReader(Cog):
return await message.channel.send(
content=None, embed=await self.blocked_game_action(message)
)
blocked_path = self.contains_blocked_paths(log_file)
if blocked_path:
return await message.channel.send(
content=None,
embed=await self.blocked_path_action(message, blocked_path),
)
elif (
is_log_file
and is_ryujinx_log_file

View file

@ -0,0 +1,47 @@
import json
import os
from robocop_ng.helpers.data_loader import read_json
def get_disabled_paths_path(bot) -> str:
return os.path.join(bot.state_dir, "data/disabled_paths.json")
def get_disabled_paths(bot) -> list[str]:
disabled_paths = read_json(bot, get_disabled_paths_path(bot))
if "paths" not in disabled_paths.keys():
return []
return disabled_paths["paths"]
def set_disabled_paths(bot, contents: list[str]):
with open(get_disabled_paths_path(bot), "w") as f:
json.dump({"paths": contents}, f)
def is_path_disabled(bot, path: str) -> bool:
for disabled_path in get_disabled_paths(bot):
if disabled_path in path.strip().lower():
return True
return False
def add_disabled_path(bot, disabled_path: str) -> bool:
disabled_path = disabled_path.strip().lower()
disabled_paths = get_disabled_paths(bot)
if disabled_path not in disabled_paths:
disabled_paths.append(disabled_path)
set_disabled_paths(bot, disabled_paths)
return True
return False
def remove_disabled_path(bot, disabled_path: str) -> bool:
disabled_path = disabled_path.strip().lower()
disabled_paths = get_disabled_paths(bot)
if disabled_path in disabled_paths:
disabled_paths.remove(disabled_path)
set_disabled_paths(bot, disabled_paths)
return True
return False

View file

@ -41,6 +41,13 @@ class LogAnalyser:
re.search("Load.*Application: Loading as [Hh]omebrew", log_file) is not None
)
@staticmethod
def get_filepaths(log_file: str) -> set[str]:
return set(
x.rstrip("\u0000")
for x in re.findall(r"(?:[A-Za-z]:)?(?:[\\/]+[^\\/:\"\r\n]+)+", log_file)
)
@staticmethod
def get_main_ro_section(log_file: str) -> Optional[dict[str, str]]:
ro_section_matches = re.findall(
@ -81,7 +88,7 @@ class LogAnalyser:
else:
app_id = ""
bids_match_all = re.findall(
r"Build ids found for title ([a-zA-Z0-9]*):[\n\r]*((?:\s+.*[\n\r]+)+)",
r"Build ids found for (?:title|application) ([a-zA-Z0-9]*):[\n\r]*((?:\s+.*[\n\r]+)+)",
log_file,
)
if bids_match_all and len(bids_match_all) > 0:
@ -443,6 +450,10 @@ class LogAnalyser:
"**⚠️ AMD GPU users should consider using Vulkan graphics backend**"
)
def __get_cpu_notes(self):
if "VirtualApple" in self._hardware_info["cpu"]:
self._notes.add("🔴 **Rosetta should be disabled**")
def __get_log_notes(self):
default_logs = ["Info", "Warning", "Error", "Guest"]
user_logs = []
@ -572,6 +583,7 @@ class LogAnalyser:
self.__get_controller_notes()
self.__get_os_notes()
self.__get_cpu_notes()
if (
self._emu_info["ryu_firmware"] == "Unknown"
@ -706,6 +718,7 @@ class LogAnalyser:
"errors": self._log_errors,
"settings": self._settings,
"app_info": self.get_app_info(self._log_text),
"paths": list(self.get_filepaths(self._log_text)),
}