Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 65 additions & 17 deletions .github/workflows/pyinstaller.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ["windows-latest"] # Currently, other OS's are not supported
os: ["windows-latest", "ubuntu-latest"]

env:
MAIN_FILE: '"./cs2tracker/__main__.py"'
NAME: '"cs2tracker"'
ICON: '"./assets/icon.png"'
ICON_INCLUDE: '"./assets/icon.png;./assets"'
DATA_DIR_INCLUDE: '"./cs2tracker/data;./data"'
NODE_MODULES_INCLUDE: '"./node_modules;./node_modules"'
WINDOWS_ICON_INCLUDE: '"./assets/icon.png;./assets"'
WINDOWS_DATA_DIR_INCLUDE: '"./cs2tracker/data;./data"'
WINDOWS_NODE_MODULES_INCLUDE: '"./node_modules;./node_modules"'
LINUX_ICON_INCLUDE: '"./assets/icon.png:./assets"'
LINUX_DATA_DIR_INCLUDE: '"./cs2tracker/data:./data"'
LINUX_NODE_MODULES_INCLUDE: '"./node_modules:./node_modules"'

steps:
- name: Checkout code
Expand All @@ -44,38 +47,83 @@ jobs:
- name: Install PyInstaller
run: pip install pyinstaller

- name: Locate eurofxref-hist.zip
- name: Locate eurofxref-hist.zip Windows
if: matrix.os == 'windows-latest'
run: |
$ZIP_PATH = python -c "import currency_converter, os; print(os.path.join(os.path.dirname(currency_converter.__file__), 'eurofxref-hist.zip'))"
echo "CURRENCY_INCLUDE='$ZIP_PATH;./currency_converter'" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "WINDOWS_CURRENCY_INCLUDE='$ZIP_PATH;./currency_converter'" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

- name: Locate nodejs-bin files
- name: Locate eurofxref-hist.zip Linux
if: matrix.os == 'ubuntu-latest'
run: |
ZIP_PATH=$(python -c "import currency_converter, os; print(os.path.join(os.path.dirname(currency_converter.__file__), 'eurofxref-hist.zip'))")
echo "LINUX_CURRENCY_INCLUDE=$ZIP_PATH:./currency_converter" >> $GITHUB_ENV

- name: Locate nodejs-bin files Windows
if: matrix.os == 'windows-latest'
run: |
$NODE_BIN_PATH = python -c "import nodejs, os; print(os.path.dirname(nodejs.__file__))"
echo "NODE_BIN_INCLUDE='$NODE_BIN_PATH;./nodejs'" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "WINDOWS_NODE_BIN_INCLUDE='$NODE_BIN_PATH;./nodejs'" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

- name: Locate nodejs-bin files Linux
if: matrix.os == 'ubuntu-latest'
run: |
NODE_BIN_PATH=$(python -c "import nodejs, os; print(os.path.dirname(nodejs.__file__))")
echo "LINUX_NODE_BIN_INCLUDE=$NODE_BIN_PATH:./nodejs" >> $GITHUB_ENV

- name: Build executable
- name: Build Windows executable
if: matrix.os == 'windows-latest'
run: |
pyinstaller --noconfirm --onefile --windowed --name ${{ env.NAME }} --icon ${{ env.ICON }} `
--add-data ${{ env.ICON_INCLUDE }} `
--add-data ${{ env.DATA_DIR_INCLUDE }} `
--add-data ${{ env.NODE_MODULES_INCLUDE }} `
--add-data ${{ env.CURRENCY_INCLUDE }} `
--add-data ${{ env.NODE_BIN_INCLUDE }} `
--add-data ${{ env.WINDOWS_ICON_INCLUDE }} `
--add-data ${{ env.WINDOWS_DATA_DIR_INCLUDE }} `
--add-data ${{ env.WINDOWS_NODE_MODULES_INCLUDE }} `
--add-data ${{ env.WINDOWS_CURRENCY_INCLUDE }} `
--add-data ${{ env.WINDOWS_NODE_BIN_INCLUDE }} `
${{ env.MAIN_FILE }}

- name: Build Linux executable
if: matrix.os == 'ubuntu-latest'
run: |
pyinstaller --noconfirm --onefile --windowed --name ${{ env.NAME }} --icon ${{ env.ICON }} \
--add-data ${{ env.LINUX_ICON_INCLUDE }} \
--add-data ${{ env.LINUX_DATA_DIR_INCLUDE }} \
--add-data ${{ env.LINUX_NODE_MODULES_INCLUDE }} \
--add-data ${{ env.LINUX_CURRENCY_INCLUDE }} \
--add-data ${{ env.LINUX_NODE_BIN_INCLUDE }} \
${{ env.MAIN_FILE }}

- name: List files in dist folder
run: ls -R dist/

- name: Zip windows executable
- name: Zip Windows executable
if: matrix.os == 'windows-latest'
run: Compress-Archive -Path "dist/cs2tracker.exe" -DestinationPath "cs2tracker-windows.zip"

- name: Generate SHA256 checksum
- name: Zip Linux executable
if: matrix.os == 'ubuntu-latest'
run: zip cs2tracker-linux.zip dist/cs2tracker

- name: Generate SHA256 checksum Windows
if: matrix.os == 'windows-latest'
run: shasum -a 256 cs2tracker-windows.zip > cs2tracker-windows.zip.sha256

- name: Upload windows executable
- name: Generate SHA256 checksum Linux
if: matrix.os == 'ubuntu-latest'
run: shasum -a 256 cs2tracker-linux.zip > cs2tracker-linux.zip.sha256

- name: Upload Windows executable
if: matrix.os == 'windows-latest'
uses: alexellis/upload-assets@0.4.0
env:
GITHUB_TOKEN: ${{ github.token }}
with:
asset_paths: '["cs2tracker-windows.zip", "cs2tracker-windows.zip.sha256"]'

- name: Upload Linux executable
if: matrix.os == 'ubuntu-latest'
uses: alexellis/upload-assets@0.4.0
env:
GITHUB_TOKEN: ${{ github.token }}
with:
asset_paths: '["cs2tracker-linux.zip", "cs2tracker-linux.zip.sha256"]'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
__pycache__
venv
venv-test
venv-wsl

# Python build output
build
Expand Down
23 changes: 20 additions & 3 deletions cs2tracker/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,36 @@
class OSType(enum.Enum):
WINDOWS = "Windows"
LINUX = "Linux"
MACOS = "MacOS"
Comment thread
ashiven marked this conversation as resolved.


OS = OSType.WINDOWS if sys.platform.startswith("win") else OSType.LINUX
PYTHON_EXECUTABLE = sys.executable
if sys.platform.startswith("win"):
OS = OSType.WINDOWS
elif sys.platform.startswith("linux"):
OS = OSType.LINUX
elif sys.platform.startswith("darwin"):
OS = OSType.MACOS
else:
raise NotImplementedError(f"Unsupported OS: {sys.platform}")


PYTHON_EXECUTABLE = sys.executable
RUNNING_IN_EXE = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")

if RUNNING_IN_EXE:
MEIPASS_DIR = sys._MEIPASS # type: ignore pylint: disable=protected-access
MODULE_DIR = MEIPASS_DIR
PROJECT_DIR = MEIPASS_DIR
APP_DATA_DIR = os.path.join(os.path.expanduser("~"), "AppData", "Local")

if OS == OSType.WINDOWS:
APP_DATA_DIR = os.path.join(os.path.expanduser("~"), "AppData", "Local")
elif OS == OSType.LINUX:
APP_DATA_DIR = os.environ.get(
"XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share")
)
else:
raise NotImplementedError(f"Unsupported OS: {OS}")

DATA_DIR = os.path.join(APP_DATA_DIR, "cs2tracker", "data")
os.makedirs(DATA_DIR, exist_ok=True)

Expand Down
84 changes: 79 additions & 5 deletions cs2tracker/scraper/background_task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from subprocess import DEVNULL, call
from subprocess import DEVNULL, STDOUT, CalledProcessError, call, check_output, run

from cs2tracker.constants import (
BATCH_FILE,
Expand All @@ -18,6 +18,18 @@
f"powershell -WindowStyle Hidden -Command \"Start-Process '{BATCH_FILE}' -WindowStyle Hidden\""
)

LINUX_BACKGROUND_TASK_SCHEDULE = "0 12 * * *"
LINUX_BACKGROUND_TASK_CMD = (
f"bash -c 'cd {PROJECT_DIR} && {PYTHON_EXECUTABLE} -m cs2tracker --only-scrape'"
)
LINUX_BACKGROUND_TASK_CMD_EXE = f"bash -c 'cd {PROJECT_DIR} && {PYTHON_EXECUTABLE} --only-scrape'"
Comment thread
ashiven marked this conversation as resolved.

if RUNNING_IN_EXE:
LINUX_CRON_JOB = f"{LINUX_BACKGROUND_TASK_SCHEDULE} {LINUX_BACKGROUND_TASK_CMD_EXE}"
else:
LINUX_CRON_JOB = f"{LINUX_BACKGROUND_TASK_SCHEDULE} {LINUX_BACKGROUND_TASK_CMD}"


console = get_console()


Expand All @@ -34,8 +46,17 @@ def identify(cls):
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
found = return_code == 0
return found
elif OS == OSType.LINUX:
try:
existing_jobs = (
check_output(["crontab", "-l"], stderr=STDOUT).decode("utf-8").strip()
)
except CalledProcessError:
existing_jobs = ""

found = LINUX_CRON_JOB in existing_jobs.splitlines()
return found
else:
# TODO: implement finder for cron jobs
return False

@classmethod
Expand Down Expand Up @@ -83,17 +104,69 @@ def _toggle_windows(cls, enabled: bool):
]
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
if return_code == 0:
console.print("[bold green][+] Background task enabled.")
console.info("Background task enabled.")
else:
console.error("Failed to enable background task.")
else:
cmd = ["schtasks", "/delete", "/tn", WIN_BACKGROUND_TASK_NAME, "/f"]
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
if return_code == 0:
console.print("[bold green][-] Background task disabled.")
console.info("Background task disabled.")
else:
console.error("Failed to disable background task.")
Comment thread
ashiven marked this conversation as resolved.

@classmethod
def _toggle_linux(cls, enabled: bool):
"""
Create or delete a daily background task that runs the scraper on Linux.

:param enabled: If True, the task will be created; if False, the task will be
deleted.
"""
try:
existing_jobs = check_output(["crontab", "-l"], stderr=STDOUT).decode("utf-8").strip()
except CalledProcessError:
existing_jobs = ""

cron_lines = existing_jobs.splitlines()

if enabled and LINUX_CRON_JOB not in cron_lines:
updated_jobs = (
existing_jobs + "\n" + LINUX_CRON_JOB + "\n"
if existing_jobs
else LINUX_CRON_JOB + "\n"
)
try:
run(
["crontab", "-"],
input=updated_jobs.encode("utf-8"),
stdout=DEVNULL,
stderr=DEVNULL,
check=True,
)
console.info("Background task enabled.")
except CalledProcessError:
console.error("Failed to enable background task.")

elif not enabled and LINUX_CRON_JOB in cron_lines:
updated_jobs = "\n".join(
line for line in cron_lines if line.strip() != LINUX_CRON_JOB
).strip()
try:
if updated_jobs:
run(
["crontab", "-"],
input=(updated_jobs + "\n").encode("utf-8"),
stdout=DEVNULL,
stderr=DEVNULL,
check=True,
)
else:
run(["crontab", "-r"], stdout=DEVNULL, stderr=DEVNULL, check=True)
console.info("Background task disabled.")
except CalledProcessError:
console.error("Failed to disable background task.")

@classmethod
def toggle(cls, enabled: bool):
"""
Expand All @@ -104,6 +177,7 @@ def toggle(cls, enabled: bool):
"""
if OS == OSType.WINDOWS:
cls._toggle_windows(enabled)
elif OS == OSType.LINUX:
cls._toggle_linux(enabled)
else:
# TODO: implement toggle for cron jobs
pass
8 changes: 7 additions & 1 deletion cs2tracker/scraper/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class CSGOTraderParser(BaseParser):
CSGOTRADER_PRICE_LIST = "https://prices.csgotrader.app/latest/{}.json"
PRICE_INFO = "Owned: {:<10} {:<10}: ${:<10} Total: ${:<10}"
NEEDS_TIMEOUT = False
SOURCES = [PriceSource.STEAM, PriceSource.BUFF163, PriceSource.YOUPIN898]
SOURCES = [PriceSource.STEAM, PriceSource.BUFF163, PriceSource.CSFLOAT]

@classmethod
def get_item_page_url(cls, item_href, source=PriceSource.STEAM):
Expand Down Expand Up @@ -176,6 +176,12 @@ def parse_item_price(cls, item_page, item_href, source=PriceSource.STEAM):
raise ValueError(
f"CSGOTrader: Could not find recent youpin898 price: {url_decoded_name}"
)
elif source == PriceSource.CSFLOAT:
price = price_info.get("price")
if not price:
raise ValueError(
f"CSGOTrader: Could not find recent csfloat price: {url_decoded_name}"
)
else:
price = price_info.get("starting_at")
if not price:
Expand Down
Loading