meow
This commit is contained in:
45
.gitea/workflows/release.yml
Normal file
45
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Create Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pnpm install --no-frozen-lockfile
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Run Semantic Release
|
||||
run: npx semantic-release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
87
.gitignore
vendored
Normal file
87
.gitignore
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Build outputs
|
||||
.millennium/
|
||||
.build
|
||||
build/
|
||||
dist/
|
||||
*.tsbuildinfo
|
||||
.tscache/
|
||||
|
||||
# Python
|
||||
.venv/
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# Package files
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 175
|
||||
}
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 tor968
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
17
README.md
Normal file
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Leetify Extension for Millennium
|
||||
|
||||
A Millennium plugin that integrates Leetify data and functionality directly into the Steam client, providing enhanced Counter-Strike statistics and profile information.
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
Before installing this plugin, ensure you have:
|
||||
|
||||
- **[Millennium](https://steambrew.app/)** installed and configured
|
||||
|
||||
|
||||
|
||||
## 🔗 Links
|
||||
|
||||
- [Millennium Framework](https://github.com/SteamClientHomebrew/Millennium)
|
||||
- [Leetify](https://leetify.com)
|
||||
- [Steam Client](https://store.steampowered.com/about/)
|
||||
83
RELEASE.md
Normal file
83
RELEASE.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Release Guide
|
||||
|
||||
This document explains how to create and manage releases for the Leetify Extension plugin.
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
The project supports two types of releases:
|
||||
|
||||
1. **Automated Releases** - Using GitHub Actions and semantic-release
|
||||
2. **Manual Releases** - Using local scripts for more control
|
||||
|
||||
## 🤖 Automated Releases (Recommended)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Repository must be hosted on GitHub
|
||||
- `GITHUB_TOKEN` secret must be configured in repository settings
|
||||
- All changes committed to `main` or `master` branch
|
||||
|
||||
### How it Works
|
||||
|
||||
1. **Commit Messages**: Use [Conventional Commits](https://www.conventionalcommits.org/) format:
|
||||
|
||||
```
|
||||
feat: add new Leetify integration feature
|
||||
fix: resolve profile loading issue
|
||||
docs: update installation instructions
|
||||
chore: update dependencies
|
||||
```
|
||||
|
||||
2. **Automatic Triggering**: Releases are triggered automatically when:
|
||||
|
||||
- Commits are pushed to `main`/`master` branch
|
||||
- Commit messages follow conventional format
|
||||
- Changes affect code (not just documentation)
|
||||
|
||||
3. **Version Bumping**: Semantic-release automatically determines version based on commit types:
|
||||
- `fix:` → Patch release (1.0.0 → 1.0.1)
|
||||
- `feat:` → Minor release (1.0.0 → 1.1.0)
|
||||
- `BREAKING CHANGE:` → Major release (1.0.0 → 2.0.0)
|
||||
|
||||
### Setting Up Automated Releases
|
||||
|
||||
1. **Configure GitHub Secrets**:
|
||||
|
||||
- Go to your repository → Settings → Secrets and variables → Actions
|
||||
- Add `GITHUB_TOKEN` (usually automatically available)
|
||||
|
||||
2. **Make a Release Commit**:
|
||||
|
||||
````bash
|
||||
git add .
|
||||
git commit -m "feat: add Leetify profile integration"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
3. **Monitor Release**:
|
||||
- Check Actions tab in GitHub for release progress
|
||||
- Release will appear in Releases section when complete
|
||||
|
||||
## 🔧 Manual Releases
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 18+ with pnpm installed
|
||||
- Python 3.7+ with pip
|
||||
- Git repository with clean working directory
|
||||
|
||||
### Creating a Manual Release
|
||||
|
||||
1. **Install Dependencies**:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Run Release Script**:
|
||||
````bash
|
||||
# Patch release (1.0.0 → 1.0.1)\n pnpm run release patch\n \n # Minor release (1.0.0 → 1.1.0)\n pnpm run release minor\n \n # Major release (1.0.0 → 2.0.0)\n pnpm run release major\n ```\n\n3. **Push Changes**:\n ```bash\n git push origin main\n git push origin v<version>\n ```\n\n4. **Create GitHub Release**:\n - Go to GitHub → Releases → \"Create a new release\"\n - Select the tag created by the script\n - Upload the generated ZIP file from `build/` directory\n - Add release notes\n\n### Available Scripts\n\n```bash\n# Build plugin package\npnpm run build-plugin\n\n# Create manual release\npnpm run release <patch|minor|major>\n\n# Sync versions between files\npnpm run sync-version <version>\n\n# Development build\npnpm run dev\n\n# Production build\npnpm run build\n```\n\n## 📦 Release Artifacts\n\nEach release creates:\n- **ZIP Package**: `leetify-extension-v<version>.zip`\n- **Updated Files**: `package.json`, `plugin.json`, `CHANGELOG.md`\n- **Git Tag**: `v<version>`\n- **GitHub Release**: With ZIP attachment and release notes\n\n### Package Contents\n```\nleetify-extension-v1.0.0.zip\n├── frontend/ # React frontend components\n├── webkit/ # Steam webkit integration\n├── backend/ # Python backend logic\n├── styles/ # CSS styles\n├── dist/ # Built assets (if exists)\n├── package.json # Node.js package configuration\n├── plugin.json # Millennium plugin configuration\n├── requirements.txt # Python dependencies\n├── README.md # Installation and usage guide\n└── LICENSE # License file\n```\n\n## 🔄 Version Management\n\n### Version Synchronization\nThe release system automatically keeps versions synchronized across:\n- `package.json` - Node.js package version\n- `plugin.json` - Millennium plugin version\n\n### Version Scheme\nFollows [Semantic Versioning](https://semver.org/):\n- **MAJOR** (X.0.0): Breaking changes, incompatible API changes\n- **MINOR** (0.X.0): New features, backwards compatible\n- **PATCH** (0.0.X): Bug fixes, backwards compatible\n\n## 🚀 Deployment Process\n\n### For Plugin Users\n1. Download latest release ZIP from GitHub Releases\n2. Extract to Steam plugins directory\n3. Enable plugin in Millennium settings\n4. Restart Steam\n\n### For Developers\n1. Clone repository\n2. Install dependencies\n3. Make changes\n4. Create release (automated or manual)\n5. Users download and install\n\n## 🛠️ Troubleshooting\n\n### Common Issues\n\n**Build Fails**:\n```bash\n# Clear cache and reinstall\npnpm clean-install\npip install --upgrade -r requirements.txt\n```\n\n**Version Sync Issues**:\n```bash\n# Manually sync versions\npnpm run sync-version 1.2.3\n```\n\n**Git Tag Conflicts**:\n```bash\n# Delete local tag\ngit tag -d v1.0.0\n\n# Delete remote tag\ngit push origin :refs/tags/v1.0.0\n```\n\n**Release Workflow Fails**:\n- Check GitHub Actions logs\n- Verify secrets are configured\n- Ensure commit messages follow conventional format\n- Check for merge conflicts\n\n### Debug Mode\n```bash\n# Enable verbose logging\nexport DEBUG=semantic-release:*\npnpm run release patch\n```\n\n## 📋 Release Checklist\n\n### Before Release\n- [ ] All tests pass\n- [ ] Documentation is updated\n- [ ] CHANGELOG.md reflects changes\n- [ ] Version compatibility verified\n- [ ] No uncommitted changes\n\n### During Release\n- [ ] Choose appropriate version bump\n- [ ] Verify build completes successfully\n- [ ] Check generated ZIP contains all files\n- [ ] Validate plugin.json and package.json versions match\n\n### After Release\n- [ ] GitHub release created successfully\n- [ ] ZIP file uploaded to release\n- [ ] Release notes are accurate\n- [ ] Installation instructions updated if needed\n- [ ] Community notified (if applicable)\n\n## 🔗 Related Links\n\n- [Conventional Commits](https://www.conventionalcommits.org/)\n- [Semantic Versioning](https://semver.org/)\n- [GitHub Actions Documentation](https://docs.github.com/en/actions)\n- [Millennium Plugin Documentation](https://docs.steambrew.app/developers/plugins/learn)\n\n---\n\n**Note**: This release system is designed specifically for Millennium plugins and follows the framework's conventions and requirements.
|
||||
````
|
||||
23
backend/logger.py
Normal file
23
backend/logger.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import PluginUtils # type: ignore[import]
|
||||
|
||||
class Logger:
|
||||
def __init__(self):
|
||||
self._logger = PluginUtils.Logger()
|
||||
|
||||
def info(self, message: str) -> None:
|
||||
"""Log an info message"""
|
||||
self._logger.log(message)
|
||||
|
||||
def error(self, message: str) -> None:
|
||||
"""Log an error message"""
|
||||
self._logger.log(f"ERROR: {message}")
|
||||
|
||||
def warning(self, message: str) -> None:
|
||||
"""Log a warning message"""
|
||||
self._logger.log(f"WARNING: {message}")
|
||||
|
||||
def debug(self, message: str) -> None:
|
||||
"""Log a debug message"""
|
||||
self._logger.log(f"DEBUG: {message}")
|
||||
|
||||
logger = Logger()
|
||||
28
backend/main.py
Normal file
28
backend/main.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import Millennium # pyright: ignore[reportMissingImports]
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Plugin:
|
||||
def _load(self) -> None:
|
||||
try:
|
||||
logger.info("Leetify Extension: Starting plugin initialization...")
|
||||
Millennium.ready()
|
||||
logger.info("Leetify Extension: Plugin loaded successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Leetify Extension: Failed to load plugin: {str(e)}")
|
||||
raise
|
||||
|
||||
def _front_end_loaded(self) -> None:
|
||||
try:
|
||||
logger.info("Leetify Extension: Frontend loaded successfully")
|
||||
# Add any frontend-specific initialization logic here if needed
|
||||
except Exception as e:
|
||||
logger.error(f"Leetify Extension: Error during frontend load: {str(e)}")
|
||||
|
||||
def _unload(self) -> None:
|
||||
try:
|
||||
logger.info("Leetify Extension: Plugin unloading...")
|
||||
# Add any cleanup logic here if needed
|
||||
logger.info("Leetify Extension: Plugin unloaded successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Leetify Extension: Error during plugin unload: {str(e)}")
|
||||
78
backend/settings.py
Normal file
78
backend/settings.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from typing import Literal
|
||||
from MillenniumUtils import ( # pyright: ignore[reportMissingImports]
|
||||
CheckBox,
|
||||
DefineSetting,
|
||||
DropDown,
|
||||
NumberTextInput,
|
||||
Settings,
|
||||
FloatSlider,
|
||||
StringTextInput,
|
||||
FloatTextInput,
|
||||
NumberSlider,
|
||||
)
|
||||
from logger import logger
|
||||
|
||||
|
||||
class PluginSettings(metaclass=Settings):
|
||||
@DefineSetting(
|
||||
name="CheckBox Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=CheckBox(),
|
||||
default=True,
|
||||
)
|
||||
def checkboxInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="Dropdown Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=DropDown(items=["String Value", False, 69]),
|
||||
default="String Value",
|
||||
)
|
||||
def dropDownInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="Float Slider Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=FloatSlider(range=(0.0, 10.0), step=0.5),
|
||||
default=0.5,
|
||||
)
|
||||
def floatSliderInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="Number Slider Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=NumberSlider(range=(0, 10), step=1),
|
||||
default=5,
|
||||
)
|
||||
def numberSliderInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="Number Text Input Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=NumberTextInput(range=(0, 10000)),
|
||||
default=1234,
|
||||
)
|
||||
def numberTextInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="String Text Input Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=StringTextInput(),
|
||||
default="Hello World!",
|
||||
)
|
||||
def stringTextInput(self):
|
||||
pass
|
||||
|
||||
@DefineSetting(
|
||||
name="Float Text Input Example",
|
||||
description="lorem ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
style=FloatTextInput(range=(0, 10000)),
|
||||
default=1234.0,
|
||||
)
|
||||
def floatTextInput(self):
|
||||
pass
|
||||
5
frontend/index.tsx
Normal file
5
frontend/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { definePlugin } from '@steambrew/client';
|
||||
|
||||
export default definePlugin(() => {
|
||||
console.log('.rip Extension: Frontend plugin initializing...');
|
||||
});
|
||||
16
frontend/settings.tsx
Normal file
16
frontend/settings.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { BindPluginSettings } from '@steambrew/client';
|
||||
|
||||
type CheckBox = true | false;
|
||||
type EnumerateInternal<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : EnumerateInternal<N, [...Acc, Acc['length']]>;
|
||||
type Enumerate<Min extends number, Max extends number> = Exclude<EnumerateInternal<Max>, EnumerateInternal<Min>> | Max;
|
||||
type NumberTextInput<Min extends number, Max extends number> = Min | Enumerate<Min, Max>;
|
||||
type DropDown<T extends readonly any[]> = T[number];
|
||||
|
||||
interface SettingsProps {
|
||||
doFrontEndCall: CheckBox;
|
||||
overrideWebkitDocument: CheckBox;
|
||||
numberTextInput: NumberTextInput<1, 100>;
|
||||
frontEndMessage: DropDown<['hello', 'hi', 'hello again', false, 69]>;
|
||||
}
|
||||
|
||||
export let PluginSettings: SettingsProps = BindPluginSettings();
|
||||
9
frontend/tsconfig.json
Normal file
9
frontend/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsxFactory": "window.SP_REACT.createElement",
|
||||
"jsxFragmentFactory": "window.SP_REACT.Fragment"
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["node_modules", "../webkit"]
|
||||
}
|
||||
38
package.json
Normal file
38
package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "millennium-ttc --build dev",
|
||||
"watch": "nodemon --watch webkit --watch frontend --ext ts,tsx,js,jsx --exec \"millennium-ttc --build dev\"",
|
||||
"build": "millennium-ttc --build prod",
|
||||
"sync-version": "npx tsx scripts/sync-version.ts 1.0.0",
|
||||
"release": "npx tsx scripts/build-plugin.ts 1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^11.1.0",
|
||||
"@semantic-release/exec": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/github": "^9.2.6",
|
||||
"@semantic-release/release-notes-generator": "^12.1.0",
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.8",
|
||||
"@types/node": "^20.11.0",
|
||||
"archiver": "^6.0.0",
|
||||
"nodemon": "^3.1.9",
|
||||
"semantic-release": "^22.0.12",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"type": "module",
|
||||
"name": "rip-extension",
|
||||
"version": "1.0.0",
|
||||
"description": "A Millennium plugin that adds .rip integration to Steam profiles",
|
||||
"main": "./frontend/index.tsx",
|
||||
"author": "mx Extension Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@steambrew/api": "^5.5.3",
|
||||
"@steambrew/client": "^5.5.3",
|
||||
"@steambrew/ttc": "^2.7.3",
|
||||
"@steambrew/webkit": "^5.5.3"
|
||||
}
|
||||
}
|
||||
7
plugin.json
Normal file
7
plugin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/SteamClientHomebrew/Millennium/main/src/sys/plugin-schema.json",
|
||||
"name": ".rip-extension",
|
||||
"common_name": "rip Extension",
|
||||
"description": "A Millennium plugin that adds csst.at integration to Steam profiles.",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
5872
pnpm-lock.yaml
generated
Normal file
5872
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
ignoredBuiltDependencies:
|
||||
- '@parcel/watcher'
|
||||
38
release.config.mjs
Normal file
38
release.config.mjs
Normal file
@@ -0,0 +1,38 @@
|
||||
export default {
|
||||
branches: ['main', 'master'],
|
||||
plugins: [
|
||||
'@semantic-release/commit-analyzer',
|
||||
'@semantic-release/release-notes-generator',
|
||||
[
|
||||
'@semantic-release/changelog',
|
||||
{
|
||||
changelogFile: 'CHANGELOG.md',
|
||||
},
|
||||
],
|
||||
[
|
||||
'@semantic-release/exec',
|
||||
{
|
||||
prepareCmd: 'npx tsx scripts/sync-version.ts ${nextRelease.version}',
|
||||
publishCmd: 'npm run build && npx tsx scripts/build-plugin.ts ${nextRelease.version}',
|
||||
},
|
||||
],
|
||||
[
|
||||
'@semantic-release/github',
|
||||
{
|
||||
assets: [
|
||||
{
|
||||
path: 'leetify-extension-*.zip',
|
||||
label: 'Leetify Extension Plugin (${nextRelease.gitTag})',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
'@semantic-release/git',
|
||||
{
|
||||
assets: ['package.json', 'plugin.json', 'CHANGELOG.md'],
|
||||
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
requests>=2.25.0
|
||||
127
scripts/build-plugin.ts
Normal file
127
scripts/build-plugin.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, mkdirSync, rmSync, cpSync, createWriteStream } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const archiver = require('archiver');
|
||||
|
||||
const version = process.argv[2];
|
||||
if (!version) {
|
||||
console.error('Version is required as first argument');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const rootDir = process.cwd();
|
||||
const releaseDir = join(rootDir, 'release');
|
||||
const zipName = `leetify-extension-v${version}.zip`;
|
||||
|
||||
console.log(`Building plugin version ${version}...`);
|
||||
|
||||
async function buildPlugin() {
|
||||
try {
|
||||
// Clean up any existing release directory
|
||||
if (existsSync(releaseDir)) {
|
||||
rmSync(releaseDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// Create release directory
|
||||
mkdirSync(releaseDir, { recursive: true });
|
||||
|
||||
// Copy necessary files and directories
|
||||
const filesToCopy = [
|
||||
{ src: '.millennium', dest: '.millennium' },
|
||||
{ src: 'backend', dest: 'backend' },
|
||||
{ src: 'plugin.json', dest: 'plugin.json' },
|
||||
{ src: 'requirements.txt', dest: 'requirements.txt' },
|
||||
{ src: 'README.md', dest: 'README.md' },
|
||||
];
|
||||
|
||||
// Check if LICENSE file exists and add it if it does
|
||||
if (existsSync(join(rootDir, 'LICENSE'))) {
|
||||
filesToCopy.push({ src: 'LICENSE', dest: 'LICENSE' });
|
||||
}
|
||||
|
||||
// Copy styles if they exist
|
||||
if (existsSync(join(rootDir, 'styles'))) {
|
||||
mkdirSync(join(releaseDir, 'static'), { recursive: true });
|
||||
cpSync(join(rootDir, 'styles'), join(releaseDir, 'static'), { recursive: true });
|
||||
}
|
||||
|
||||
// Copy all files
|
||||
for (const { src, dest } of filesToCopy) {
|
||||
const srcPath = join(rootDir, src);
|
||||
const destPath = join(releaseDir, dest);
|
||||
|
||||
if (existsSync(srcPath)) {
|
||||
cpSync(srcPath, destPath, { recursive: true });
|
||||
console.log(`Copied ${src} to release directory`);
|
||||
} else {
|
||||
console.warn(`Warning: ${src} does not exist, skipping...`);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Git metadata
|
||||
console.log('Generating Git metadata...');
|
||||
try {
|
||||
const commitId = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
||||
const pluginId = execSync('git rev-list --max-parents=0 HEAD', { encoding: 'utf8' }).trim();
|
||||
|
||||
const metadata = {
|
||||
id: pluginId,
|
||||
commitId: commitId,
|
||||
};
|
||||
|
||||
const metadataPath = join(releaseDir, 'metadata.json');
|
||||
|
||||
// Write metadata.json to release directory
|
||||
require('fs').writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
||||
console.log('Generated metadata.json with Git information');
|
||||
} catch (error) {
|
||||
console.warn('Warning: Could not generate Git metadata:', error);
|
||||
}
|
||||
|
||||
// Create zip file using archiver for cross-platform compatibility
|
||||
console.log('Creating zip file...');
|
||||
const zipPath = join(rootDir, zipName);
|
||||
const output = createWriteStream(zipPath);
|
||||
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||
|
||||
// Listen for archive events
|
||||
archive.on('error', (err: any) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
output.on('close', () => {
|
||||
console.log(`✅ Successfully created ${zipName}`);
|
||||
// Clean up release directory
|
||||
rmSync(releaseDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
// Pipe archive data to the file
|
||||
archive.pipe(output);
|
||||
|
||||
// Add all files from release directory to archive
|
||||
archive.directory(releaseDir, false);
|
||||
|
||||
// Finalize the archive
|
||||
await archive.finalize();
|
||||
} catch (error) {
|
||||
console.error('❌ Build failed:', error);
|
||||
|
||||
// Clean up on error
|
||||
if (existsSync(releaseDir)) {
|
||||
rmSync(releaseDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the build
|
||||
buildPlugin().catch((error) => {
|
||||
console.error('❌ Build failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
56
scripts/sync-version.ts
Normal file
56
scripts/sync-version.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const rootDir = join(__dirname, '..');
|
||||
|
||||
interface PackageJson {
|
||||
version: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface PluginJson {
|
||||
version: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function syncVersion(newVersion: string = '1.0.0'): void {
|
||||
if (!newVersion) {
|
||||
console.error('Error: Version argument is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Update package.json
|
||||
const packagePath: string = join(rootDir, 'package.json');
|
||||
const packageJson: PackageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
||||
packageJson.version = newVersion;
|
||||
writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
console.log(`✓ Updated package.json to version ${newVersion}`);
|
||||
|
||||
// Update plugin.json
|
||||
const pluginPath: string = join(rootDir, 'plugin.json');
|
||||
const pluginJson: PluginJson = JSON.parse(readFileSync(pluginPath, 'utf8'));
|
||||
pluginJson.version = newVersion;
|
||||
writeFileSync(pluginPath, JSON.stringify(pluginJson, null, '\t') + '\n');
|
||||
console.log(`✓ Updated plugin.json to version ${newVersion}`);
|
||||
|
||||
console.log(`✅ Version synchronization complete: ${newVersion}`);
|
||||
} catch (error: any) {
|
||||
console.error('Error syncing version:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (import.meta.url.startsWith('file:') && process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
// Get version from command line argument
|
||||
const newVersion: string = process.argv[2];
|
||||
syncVersion(newVersion);
|
||||
}
|
||||
17
scripts/tsconfig.json
Normal file
17
scripts/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": false,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["*.ts"],
|
||||
"exclude": ["node_modules", "build"]
|
||||
}
|
||||
0
styles/styles.css
Normal file
0
styles/styles.css
Normal file
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": ".millennium/Dist",
|
||||
"module": "ESNext",
|
||||
"target": "ES2020",
|
||||
"jsx": "react",
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
89
webkit/index.tsx
Normal file
89
webkit/index.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
type Millennium = {
|
||||
callServerMethod: (methodName: string, kwargs?: any) => Promise<any>;
|
||||
findElement: (privateDocument: Document, querySelector: string, timeOut?: number) => Promise<NodeListOf<Element>>;
|
||||
};
|
||||
|
||||
declare const Millennium: Millennium;
|
||||
|
||||
export default async function WebkitMain() {
|
||||
try {
|
||||
|
||||
if (typeof Millennium === 'undefined') {
|
||||
console.error('csst.at probably down idk...');
|
||||
return;
|
||||
}
|
||||
|
||||
const styles = `
|
||||
.rip-btn {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
color: #FFF;
|
||||
font-weight: 800;
|
||||
transition: all 0.5s cubic-bezier(.23, 1, .32, 1);
|
||||
background-color: #2d243aff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.rip-btn:hover {
|
||||
background-color: #FFF;
|
||||
color: #2d243aff;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
}`;
|
||||
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.innerText = styles;
|
||||
document.head.appendChild(styleSheet);
|
||||
|
||||
const rightCol = await Millennium.findElement(document, '.profile_rightcol');
|
||||
|
||||
if (rightCol.length > 0) {
|
||||
const parser = new DOMParser();
|
||||
const profileUrl = `${window.location.href}/?xml=1`;
|
||||
|
||||
const profileResponse = await fetch(profileUrl);
|
||||
if (!profileResponse.ok) {
|
||||
console.error(`Leetify: Failed to fetch profile data: ${profileResponse.status} ${profileResponse.statusText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const profileXmlText = await profileResponse.text();
|
||||
const profileXmlDoc = parser.parseFromString(profileXmlText, 'application/xml');
|
||||
|
||||
const steamID64 = profileXmlDoc.querySelector('steamID64')?.textContent || '0';
|
||||
|
||||
if (!steamID64 || steamID64 === '0') {
|
||||
console.error('Leetify: Could not parse steamID64 from URL.');
|
||||
return;
|
||||
}
|
||||
|
||||
const statsContainer = document.createElement('div');
|
||||
statsContainer.className = 'account-row';
|
||||
|
||||
const button = document.createElement('div');
|
||||
button.className = 'rip-btn';
|
||||
|
||||
button.textContent = '.rip';
|
||||
button.onclick = () => {
|
||||
window.open(`https://steamcommunity.rip/id/${steamID64}`, '_self', 'noopener,noreferrer');
|
||||
};
|
||||
|
||||
statsContainer.appendChild(button);
|
||||
rightCol[0].insertBefore(statsContainer, rightCol[0].children[1]);
|
||||
} else {
|
||||
console.error('Leetify: Parent container ".profile_rightcol" not found');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Leetify: Error in WebkitMain:', error);
|
||||
console.error('Leetify: Stack trace:', error.stack);
|
||||
}
|
||||
}
|
||||
20
webkit/settings.tsx
Normal file
20
webkit/settings.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { BindPluginSettings } from '@steambrew/webkit';
|
||||
|
||||
type CheckBox = true | false;
|
||||
|
||||
type EnumerateInternal<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : EnumerateInternal<N, [...Acc, Acc['length']]>;
|
||||
|
||||
type Enumerate<Min extends number, Max extends number> = Exclude<EnumerateInternal<Max>, EnumerateInternal<Min>> | Max;
|
||||
|
||||
type NumberTextInput<Min extends number, Max extends number> = Min | Enumerate<Min, Max>;
|
||||
|
||||
type DropDown<T extends readonly any[]> = T[number];
|
||||
|
||||
interface SettingsProps {
|
||||
doFrontEndCall: CheckBox;
|
||||
overrideWebkitDocument: CheckBox;
|
||||
numberTextInput: NumberTextInput<1, 100>;
|
||||
frontEndMessage: DropDown<['hello', 'hi', 'hello again', false, 69]>;
|
||||
}
|
||||
|
||||
export let PluginSettings: SettingsProps = BindPluginSettings();
|
||||
9
webkit/tsconfig.json
Normal file
9
webkit/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["node_modules", "../frontend"]
|
||||
}
|
||||
Reference in New Issue
Block a user