wip: repo refactor
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
dist
|
||||
node_modules
|
||||
coverage
|
||||
12
.eslintrc.cjs
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
extends: ["@recodive/eslint-config"],
|
||||
overrides: [
|
||||
{
|
||||
extends: ["plugin:@typescript-eslint/disable-type-checked"],
|
||||
files: ["./**/*.{cjs,js,jsx}"],
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
};
|
||||
30
.github/CONTRIBUTING.md
vendored
@@ -1,30 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
## Requiered knowledge
|
||||
|
||||
- JavaScript
|
||||
- html5
|
||||
- NodeJS
|
||||
|
||||
Additional:
|
||||
|
||||
- CSS
|
||||
- [VueJS](https://vuejs.org/)
|
||||
- [ElectronJS](https://electronjs.org/)
|
||||
- [NPMjs](https://www.npmjs.com/)
|
||||
|
||||
A source code editor is also requiered. We recommend [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
### Installing the components
|
||||
|
||||
1. Install [Git](https://git-scm.com/)
|
||||
2. Install [Node](https://nodejs.org/en/)
|
||||
|
||||
### Cloning the project
|
||||
|
||||
1. Fork the [repository](https://github.com/PreMiD/PreMiD)
|
||||
2. Open a terminal and type `git clone https://github.com/PreMiD/PreMiD`
|
||||
|
||||
### Coding your vision
|
||||
|
||||
Please keep the structure. We don't want to disorganize our project. Chaotic files may not be accepted.
|
||||
BIN
.github/Electron/Chrome_bsp.png
vendored
|
Before Width: | Height: | Size: 332 KiB |
BIN
.github/Electron/PMD_Banner.png
vendored
|
Before Width: | Height: | Size: 682 KiB |
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
<!-- NOTE -->
|
||||
<!-- Keep all presence related bugs in our Presences repository! -->
|
||||
<!-- https://github.com/PreMiD/Presences/issues -->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for PreMiD
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
BIN
.github/Logo.png
vendored
|
Before Width: | Height: | Size: 11 KiB |
BIN
.github/Patreon.png
vendored
|
Before Width: | Height: | Size: 3.0 KiB |
1
.github/PayPal.svg
vendored
|
Before Width: | Height: | Size: 5.1 KiB |
13
.github/SUPPORT.md
vendored
@@ -1,13 +0,0 @@
|
||||
# How to get support
|
||||
|
||||
## Take a look at the [wiki](https://wiki.premid.app)
|
||||
Our GitHub wiki is full of information around PreMiD.<br>
|
||||
Take a look and feel free to contribute if you want to add something new.
|
||||
|
||||
## [Open a issue](https://github.com/PreMiD/PreMiD/issues/new/choose) on [GitHub](https://github.com/PreMiD/PreMiD)
|
||||
Simply open a issue if you don't feel allright.<br>
|
||||
*Aand there he goes...*
|
||||
|
||||
## Ask a staff member in [#support](https://discord.premid.app)
|
||||
The team is ready to tell you the secrets of the underworld.<br>
|
||||
Join our [Discord server](https://discord.premid.app) and find out what we're hiding.
|
||||
BIN
.github/TwitterButton.png
vendored
|
Before Width: | Height: | Size: 4.1 KiB |
20
.github/dependabot.yml
vendored
@@ -1,20 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
commit-message:
|
||||
prefix: chore
|
||||
include: scope
|
||||
labels:
|
||||
- "dependencies"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
commit-message:
|
||||
prefix: chore
|
||||
include: scope
|
||||
labels:
|
||||
- "dependencies"
|
||||
BIN
.github/example.png
vendored
|
Before Width: | Height: | Size: 332 KiB |
5
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>Recodive/Recodive:renovate-config"],
|
||||
"automerge": false
|
||||
}
|
||||
25
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Build, Lint and Test
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: package.json
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
#TODO: Add Docker Build Step
|
||||
#build-docker:
|
||||
98
.github/workflows/deploy.yml
vendored
@@ -1,98 +0,0 @@
|
||||
name: DePloY
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
env:
|
||||
NODE_ENV: DePloY
|
||||
jobs:
|
||||
package:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
npm i
|
||||
npm i -g typescript rimraf
|
||||
- name: Prepare to package
|
||||
run: npm run init
|
||||
- name: Package
|
||||
run: |
|
||||
npm run pkg
|
||||
rimraf dist/app
|
||||
node util/zip dist ${{ matrix.os }}.zip --zip
|
||||
- name: Upload bundle
|
||||
env:
|
||||
SSHHOST: ${{ secrets.MAIN_HOST }}
|
||||
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
run: |
|
||||
tsc util/uploadFile util/zip
|
||||
node util/uploadFile ${{ matrix.os }}.zip /home/PreMiD/download/util/${{ matrix.os }}.zip
|
||||
createInstallers:
|
||||
runs-on: "ubuntu-latest"
|
||||
needs: package
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo npm i
|
||||
sudo npm i -g typescript
|
||||
- name: Download InstallBuilder
|
||||
run: |
|
||||
wget https://clients.bitrock.com/installbuilder/installbuilder-enterprise-20.12.0-linux-x64-installer.run
|
||||
chmod u+x installbuilder-enterprise-20.12.0-linux-x64-installer.run
|
||||
- name: Install InstallBuilder
|
||||
run: |
|
||||
./installbuilder-enterprise-20.12.0-linux-x64-installer.run --installer-language en --prefix ./installbuilder --mode unattended
|
||||
echo "${{ secrets.IBLICENSE }}" > ./installbuilder/license.xml
|
||||
- name: Prepare Upgrade Installer
|
||||
run: |
|
||||
tsc util/prepare
|
||||
node util/prepare
|
||||
- name: Create Upgrade Installer (MacOS 64bit)
|
||||
run: |
|
||||
installbuilder/bin/builder build installer_assets/PreMiD-Upgrade.xml osx
|
||||
- name: Create Upgrade Installer (Windows)
|
||||
run: |
|
||||
installbuilder/bin/builder build installer_assets/PreMiD-Upgrade.xml windows
|
||||
- name: Upload files
|
||||
env:
|
||||
SSHHOST: ${{ secrets.MAIN_HOST }}
|
||||
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
run: |
|
||||
tsc util/uploadFile util/zip
|
||||
node util/uploadFile dist/installer/upgrader.exe /home/PreMiD/download/upgrader.exe
|
||||
node util/uploadFile dist/installer/upgrader.app.zip /home/PreMiD/download/util/upgrader.app.zip
|
||||
- name: Finalize build
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.MAIN_HOST }}
|
||||
username: ${{ secrets.SSH_USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
script: |
|
||||
cd /home/PreMiD/download/util
|
||||
unzip upgrader.app.zip
|
||||
tar -czvf upgrader.app.tgz upgrader.app
|
||||
mv upgrader.app.tgz ../
|
||||
rm -rf upgrader.app upgrader.app.zip
|
||||
unzip windows-latest.zip
|
||||
cd windows-latest/PreMiD-win32-x64/
|
||||
zip -r ../../PreMiD-win32-x64.zip .
|
||||
mv ../../PreMiD-win32-x64.zip /home/PreMiD/download/
|
||||
cd ../PreMiD-win32-ia32/
|
||||
zip -r ../../PreMiD-win32-x86.zip .
|
||||
mv ../../PreMiD-win32-x86.zip /home/PreMiD/download/
|
||||
cd ../..
|
||||
rm -rf windows-latest windows-latest.zip
|
||||
unzip macOS-latest.zip
|
||||
cd macOS-latest/PreMiD-darwin-x64/
|
||||
zip -r ../../PreMiD-darwin-x64.zip .
|
||||
mv ../../PreMiD-darwin-x64.zip /home/PreMiD/download/
|
||||
cd ../..
|
||||
rm -rf macOS-latest macOS-latest.zip
|
||||
2
.gitignore
vendored
@@ -18,3 +18,5 @@ src/update.ini
|
||||
*.app
|
||||
*.xml.backup
|
||||
*.js
|
||||
|
||||
coverage
|
||||
2
.husky/post-checkout
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
if diff --quiet HEAD@{1} HEAD -- package.json; then else pnpm i --frozen-lockfile; fi
|
||||
2
.husky/pre-commit
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pnpm run lint
|
||||
2
.husky/pre-push
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
pnpm run test
|
||||
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
*.js
|
||||
*.cjs
|
||||
*.ts
|
||||
*.vue
|
||||
1
.prettierrc.cjs
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@recodive/configs").default.prettier;
|
||||
22
@types/PreMiD/ExtensionSettings.d.ts
vendored
@@ -1,22 +0,0 @@
|
||||
export default interface ExtensionSettings {
|
||||
/**
|
||||
* If extension is enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Autolaunch enabled
|
||||
*/
|
||||
autoLaunch: boolean;
|
||||
/**
|
||||
* Media keys enabled
|
||||
*/
|
||||
mediaKeys: boolean;
|
||||
/**
|
||||
* title menubar (TrayTitle)
|
||||
*/
|
||||
titleMenubar: boolean;
|
||||
/**
|
||||
* language of extension
|
||||
*/
|
||||
language: string;
|
||||
}
|
||||
16
@types/PreMiD/Presence.d.ts
vendored
@@ -1,16 +0,0 @@
|
||||
import * as Discord from "discord-rpc";
|
||||
|
||||
export default interface Presence {
|
||||
/**
|
||||
* Client ID of presence
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Rich Procedual call connection
|
||||
*/
|
||||
rpc: Discord.Client;
|
||||
/**
|
||||
* Connection ready?
|
||||
*/
|
||||
ready: Boolean;
|
||||
}
|
||||
33
@types/PreMiD/PresenceData.d.ts
vendored
@@ -1,33 +0,0 @@
|
||||
import * as Discord from "discord-rpc";
|
||||
|
||||
export default interface PresenceData {
|
||||
/**
|
||||
* Client ID of presence
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Tray title to be shown in Mac OS tray
|
||||
*/
|
||||
trayTitle: string;
|
||||
/**
|
||||
* service name of presence
|
||||
* @deprecated
|
||||
*/
|
||||
service: string;
|
||||
/**
|
||||
* Determines if the service is currently playing something back or not, if false it will automatically hide after 1 minute
|
||||
*/
|
||||
playback: boolean;
|
||||
/**
|
||||
* Discord Presence which gets sent directly to Discord app
|
||||
*/
|
||||
presenceData: Discord.Presence;
|
||||
/**
|
||||
* Determines if the service should be hidden (clearActivity)
|
||||
*/
|
||||
hidden: boolean;
|
||||
/**
|
||||
* Determines if the service is mediaKey able / uses them
|
||||
*/
|
||||
mediaKeys: boolean;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
* @Timeraa
|
||||
* @Timeraa
|
||||
@@ -2,65 +2,38 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at contact@premid.app or by contacting a staff member on our [Discord server](https://discord.premid.app). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@premid.app or by contacting a staff member on our [Discord server](https://discord.premid.app). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
46
README.md
@@ -1,46 +0,0 @@
|
||||
<div align="center">
|
||||
|
||||
<img src=".github/Logo.png" width="150px" draggable="false"><br>
|
||||
|
||||
# PreMiD
|
||||
|
||||
## Your Rich Presence for web services!
|
||||
|
||||

|
||||

|
||||

|
||||
[](https://chrome.google.com/webstore/detail/premid/agjnjboanicjcpenljmaaigopkgdnihi)
|
||||

|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FPreMiD%2FPreMiD?ref=badge_shield)
|
||||
|
||||
<img src=".github/example.png" draggable="false"><br>
|
||||
|
||||
# About
|
||||
|
||||
**PreMiD** is a simple, configurable utility that allows you to show what you're doing on the web in your Discord **now playing status**. It supports many different websites, and will support multiple users watching the same content simultaneously in an upcoming update.
|
||||
|
||||
# Features
|
||||
|
||||
· Displays your current web service in Discord as your status.<br>
|
||||
· Grants full control over Presences.<br>
|
||||
· Supports over 1,000 web services, still rising!<br>
|
||||
· _Watch parties and more are coming soon!_
|
||||
|
||||
# Installation/Troubleshooting
|
||||
|
||||
### Installation instructions, Troubleshooting guides etc. can be located at our [**docs**](https://docs.premid.app).
|
||||
|
||||
# Support us
|
||||
|
||||
<div>
|
||||
<a target="_blank" href="https://www.patreon.com/bePatron?u=4610890" data-patreon-widget-type="become-patron-button" title="Support me on Patreon!">
|
||||
<img height="75px" draggable="false" src=".github/Patreon.png">
|
||||
</a>
|
||||
<a target="_blank" href="https://discord.premid.app/" title="Join our Discord!">
|
||||
<img src="https://discordapp.com/api/guilds/493130730549805057/widget.png?style=banner2" height="76px" draggable="false" alt="Join our Discord!">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## License
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FPreMiD%2FPreMiD?ref=badge_large)
|
||||
23
apps/pd/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM node:20-alpine as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json pnpm-lock.yaml /app/
|
||||
|
||||
RUN corepack enable && \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN pnpm --filter @premid/pd build && \
|
||||
pnpm deploy --filter @premid/pd deploy
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/deploy /app
|
||||
|
||||
USER node
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
10
apps/pd/environment.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
// env.d.ts
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NODE_ENV?: "development" | "production";
|
||||
REDIS_URL?: string;
|
||||
PORT?: string;
|
||||
HOST?: string;
|
||||
}
|
||||
}
|
||||
25
apps/pd/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@premid/pd",
|
||||
"version": "1.0.0",
|
||||
"description": "A small service to shorten image urls to get around Discord's 256 character limit",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps .",
|
||||
"dev": "node --watch --enable-source-maps .",
|
||||
"build": "tsc -p tsconfig.app.json"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@fastify/rate-limit": "^9.1.0",
|
||||
"@keyv/redis": "^2.8.4",
|
||||
"fastify": "^4.26.0",
|
||||
"got": "^14.2.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"ipaddr.js": "^2.1.0",
|
||||
"keyv": "^4.5.4",
|
||||
"nanoid": "^5.0.5"
|
||||
}
|
||||
}
|
||||
3
apps/pd/src/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createKeyv from "./functions/createKeyv.js";
|
||||
|
||||
export const keyv = createKeyv();
|
||||
9
apps/pd/src/functions/createKeyv.test.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
test("should return keyv instance", async () => {
|
||||
const { default: createKeyv } = await import("../functions/createKeyv.js"),
|
||||
|
||||
keyv = createKeyv();
|
||||
|
||||
expect(keyv).toStrictEqual(expect.any(Object));
|
||||
});
|
||||
11
apps/pd/src/functions/createKeyv.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import Keyv from "keyv";
|
||||
|
||||
export default function createKeyv() {
|
||||
const keyv = new Keyv();
|
||||
|
||||
keyv.on("error", (error) => {
|
||||
console.error("Keyv connection error:", error);
|
||||
});
|
||||
|
||||
return keyv;
|
||||
}
|
||||
53
apps/pd/src/functions/createServer.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import cors from "@fastify/cors";
|
||||
import ratelimit from "@fastify/rate-limit";
|
||||
import fastify from "fastify";
|
||||
|
||||
import createFromBase64 from "../routes/createFromBase64.js";
|
||||
import createFromImage from "../routes/createFromImage.js";
|
||||
import createShortenedLink from "../routes/createShortenedLink.js";
|
||||
import getFullLink from "../routes/getFullLink.js";
|
||||
|
||||
export async function createServer() {
|
||||
const server = fastify({
|
||||
trustProxy: true,
|
||||
});
|
||||
|
||||
await server.register(cors, {
|
||||
methods: ["GET"],
|
||||
origin: "*",
|
||||
});
|
||||
|
||||
await server.register(ratelimit, {
|
||||
max: 25,
|
||||
nameSpace: "pd",
|
||||
// TODO
|
||||
/* redis, */
|
||||
timeWindow: "1 minute",
|
||||
});
|
||||
|
||||
server.get("/create/image", createFromImage);
|
||||
server.get("/create/base64", createFromBase64);
|
||||
server.get("/create/*", createShortenedLink);
|
||||
|
||||
server.get(
|
||||
"/*",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
getFullLink,
|
||||
);
|
||||
|
||||
server.get(
|
||||
"/health",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
(_, reply) => reply.status(204).send(),
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
31
apps/pd/src/functions/getCloudFlareAddresses.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import got from "got";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("getCloudFlareAddresses", async () => {
|
||||
it("should return an array of CIDR objects", async () => {
|
||||
const { default: getCloudFlareAddr } = await import("../functions/getCloudFlareAddresses.js");
|
||||
|
||||
vi.spyOn(got, "get").mockResolvedValue({
|
||||
body: "0.0.0.0\n0.0.0.0",
|
||||
});
|
||||
|
||||
const result = await getCloudFlareAddr();
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"ipv4Prefix": "0.0.0.0",
|
||||
},
|
||||
{
|
||||
"ipv4Prefix": "0.0.0.0",
|
||||
},
|
||||
{
|
||||
"ipv6Prefix": "0.0.0.0",
|
||||
},
|
||||
{
|
||||
"ipv6Prefix": "0.0.0.0",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
9
apps/pd/src/functions/getCloudFlareAddresses.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import got from "got";
|
||||
|
||||
import { CIDR } from "./isInCidRange.js";
|
||||
|
||||
export default async function getCloudFlareAddr(): Promise<CIDR> {
|
||||
const [resv4, resv6] = await Promise.all([got.get("https://www.cloudflare.com/ips-v4"), got.get("https://www.cloudflare.com/ips-v6")]);
|
||||
|
||||
return [...resv4.body.split("\n").map(r => ({ ipv4Prefix: r })), ...resv6.body.split("\n").map(r => ({ ipv6Prefix: r }))];
|
||||
}
|
||||
31
apps/pd/src/functions/isInCidRange.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import isInCIDRRange from "./isInCidRange.js";
|
||||
|
||||
test("isInCIDRRange - IPv4 - in range", async () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }],
|
||||
ip = "192.0.2.123",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv4 - not in range", async () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }],
|
||||
ip = "192.0.3.123",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv6 - in range", async () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }],
|
||||
ip = "2001:db8::1234",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("isInCIDRRange - IPv6 - not in range", async () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }],
|
||||
ip = "2001:db9::1234",
|
||||
result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
26
apps/pd/src/functions/isInCidRange.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import ipaddr from "ipaddr.js";
|
||||
|
||||
export default function isInCIDRRange(CIDRs: CIDR, ip: string) {
|
||||
const parsed = ipaddr.parse(ip);
|
||||
|
||||
for (const CIDR of CIDRs.filter((c) => {
|
||||
if (parsed.kind() === "ipv4" && "ipv4Prefix" in c) return true;
|
||||
else if (parsed.kind() === "ipv6" && "ipv6Prefix" in c) return true;
|
||||
else return false;
|
||||
})) {
|
||||
const check = parsed.match(ipaddr.parseCIDR("ipv4Prefix" in CIDR ? CIDR.ipv4Prefix : CIDR.ipv6Prefix));
|
||||
|
||||
if (check) return check;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export type CIDR = (
|
||||
| {
|
||||
ipv4Prefix: string;
|
||||
}
|
||||
| {
|
||||
ipv6Prefix: string;
|
||||
}
|
||||
)[];
|
||||
14
apps/pd/src/index.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
|
||||
test("/health", async () => {
|
||||
const server = await createServer(),
|
||||
result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/health",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(204);
|
||||
expect(result.body).toBe("");
|
||||
});
|
||||
12
apps/pd/src/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
|
||||
if (!process.env.REDIS_URL) console.log("WARNING: No REDIS_URL environment variable set");
|
||||
|
||||
export const server = await createServer();
|
||||
|
||||
const url = await server.listen({
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
port: Number.parseInt(process.env.PORT ?? "80"),
|
||||
});
|
||||
|
||||
console.log(`Server listening at ${url}`);
|
||||
7
apps/pd/src/routes/createFromBase64.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
console.log("createFromBase64");
|
||||
};
|
||||
|
||||
export default handler;
|
||||
7
apps/pd/src/routes/createFromImage.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
console.log("createFromImage");
|
||||
};
|
||||
|
||||
export default handler;
|
||||
64
apps/pd/src/routes/createShortenedLink.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe("/create", async () => {
|
||||
const server = await createServer();
|
||||
|
||||
it("should return a 400 status code when no URL is provided", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"Invalid URL"');
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is too short", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/https://www.google.com",
|
||||
});
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"URL is too short"');
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is invalid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/file://www.googl${"e".repeat(256)}`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot('"Invalid URL"');
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"e".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid and already exists", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
const { body } = result,
|
||||
result2 = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result2.statusCode).toBe(200);
|
||||
expect(result2.body).toStrictEqual(body);
|
||||
});
|
||||
});
|
||||
33
apps/pd/src/routes/createShortenedLink.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { keyv } from "../constants.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
const url = request.url.replace("/create/", "").trim();
|
||||
|
||||
if (url.length === 0) return reply.status(400).send("Invalid URL");
|
||||
|
||||
if (url.length < 256) return reply.status(400).send("URL is too short");
|
||||
|
||||
const urlObject = new URL(url);
|
||||
if (!["http:", "https:"].includes(urlObject.protocol)) return reply.status(400).send("Invalid URL");
|
||||
|
||||
const keyvUrl = (await keyv.get(url)) as string | undefined;
|
||||
|
||||
void reply.header("Cache-control", "public, max-age=1800");
|
||||
|
||||
if (keyvUrl) {
|
||||
await Promise.all([keyv.set(url, keyvUrl, 1800), keyv.set(keyvUrl, url, 1800)]);
|
||||
|
||||
return reply.send(process.env.BASE_URL + keyvUrl);
|
||||
}
|
||||
|
||||
const uniqueId = nanoid(10);
|
||||
|
||||
await Promise.all([keyv.set(url, uniqueId, 1800), keyv.set(uniqueId, url, 1800)]);
|
||||
|
||||
return reply.send(process.env.BASE_URL + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
7
apps/pd/src/routes/getFullLink.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RouteHandlerMethod } from "fastify";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
console.log("getFullLink");
|
||||
};
|
||||
|
||||
export default handler;
|
||||
8
apps/pd/tsconfig.app.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"composite": true
|
||||
}
|
||||
}
|
||||
1
apps/pd/tsconfig.app.tsbuildinfo
Normal file
7
apps/pd/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"include": ["environment.d.ts"],
|
||||
"compilerOptions": {
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
6
docker-compose.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: premid
|
||||
services:
|
||||
redis:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- 6379:6379
|
||||
@@ -1,416 +0,0 @@
|
||||
<project>
|
||||
<shortName>PreMiD</shortName>
|
||||
<fullName>PreMiD</fullName>
|
||||
<version>VERSION</version>
|
||||
<installerFilename>upgrader.${platform_exec_suffix}</installerFilename>
|
||||
<debugLevel>0</debugLevel>
|
||||
<licenseFile>../LICENSE</licenseFile>
|
||||
<leftImage>leftSide.png</leftImage>
|
||||
<logoImage>logo.png</logoImage>
|
||||
<splashImage>logo.png</splashImage>
|
||||
<componentList>
|
||||
<component>
|
||||
<name>default</name>
|
||||
<description>Default Component</description>
|
||||
<canBeEdited>1</canBeEdited>
|
||||
<selected>1</selected>
|
||||
<show>1</show>
|
||||
<folderList>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfiles</name>
|
||||
<platforms>all</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux</name>
|
||||
<platforms>linux</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux64</name>
|
||||
<platforms>linux-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows</name>
|
||||
<platforms>windows</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows64</name>
|
||||
<platforms>windows-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfilesosx</name>
|
||||
<platforms>osx</platforms>
|
||||
</folder>
|
||||
</folderList>
|
||||
<startMenuShortcutList>
|
||||
<startMenuShortcut>
|
||||
<comment>Uninstall ${product_fullname}</comment>
|
||||
<name>Uninstall ${product_fullname}</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/${uninstallerName}.exe</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath>${installdir}/</windowsPath>
|
||||
</startMenuShortcut>
|
||||
<startMenuShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</startMenuShortcut>
|
||||
</startMenuShortcutList>
|
||||
</component>
|
||||
</componentList>
|
||||
<initializationActionList>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${windows_folder_appdata}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${platform_install_prefix}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
</initializationActionList>
|
||||
<readyToInstallActionList>
|
||||
<actionGroup>
|
||||
<customErrorMessage>Couldn't download release... Try again later.</customErrorMessage>
|
||||
<progressText>Downloading latest release...</progressText>
|
||||
<actionList>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<width>450</width>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x64</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-ia32.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x86</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-darwin-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<actionList>
|
||||
<!-- Remove the old ARP Entry
|
||||
Get the old version -->
|
||||
<registryGet>
|
||||
<key>HKEY_LOCAL_MACHINE\Software\${project.windowsSoftwareRegistryPrefix}</key>
|
||||
<name>Version</name>
|
||||
<variable>oldVersion</variable>
|
||||
</registryGet>
|
||||
|
||||
<!-- Delete the old ARP registry keys -->
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Management\ARPCache\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest type="windows"/>
|
||||
<isTrue value="${isUpgradeMode}"/>
|
||||
</ruleList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</readyToInstallActionList>
|
||||
<postInstallationActionList>
|
||||
<unzip>
|
||||
<addToUninstaller>1</addToUninstaller>
|
||||
<destinationDirectory>${installdir}</destinationDirectory>
|
||||
<progressText>Extracting release...</progressText>
|
||||
<zipFile>${system_temp_directory}/PreMiD-release.zip</zipFile>
|
||||
</unzip>
|
||||
<addDirectoriesToUninstaller>
|
||||
<addContents>1</addContents>
|
||||
<files>${installdir}/</files>
|
||||
</addDirectoriesToUninstaller>
|
||||
</postInstallationActionList>
|
||||
<preUninstallationActionList>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</preUninstallationActionList>
|
||||
<compressionAlgorithm>lzham-ultra</compressionAlgorithm>
|
||||
<createOsxBundleZip>1</createOsxBundleZip>
|
||||
<defaultInstallationMode>unattended</defaultInstallationMode>
|
||||
<deleteOnExit>1</deleteOnExit>
|
||||
<description>Rich Presence for web services.</description>
|
||||
<disableSplashScreen>1</disableSplashScreen>
|
||||
<enableRollback>0</enableRollback>
|
||||
<enableSslSupport>1</enableSslSupport>
|
||||
<enableTimestamp>1</enableTimestamp>
|
||||
<installationScope>user</installationScope>
|
||||
<installationType>upgrade</installationType>
|
||||
<licenseFileEncoding>utf-8</licenseFileEncoding>
|
||||
<osxApplicationBundleIcon>appIcon.icns</osxApplicationBundleIcon>
|
||||
<osxApplicationBundleIdentifier>eu.Timeraa.PreMiD</osxApplicationBundleIdentifier>
|
||||
<osxPlatforms>osx-intel osx-x86_64</osxPlatforms>
|
||||
<osxUninstallerApplicationBundleIcon>appIcon.icns</osxUninstallerApplicationBundleIcon>
|
||||
<outputDirectory>../dist/installer</outputDirectory>
|
||||
<overwritePolicy>onlyIfNewer</overwritePolicy>
|
||||
<productDisplayIcon>C:/Users/metzf/Documents/Development/PreMiD/PreMiD/installer_assets/appIcon.ico</productDisplayIcon>
|
||||
<productUrlHelpLink>https://discord.premid.app</productUrlHelpLink>
|
||||
<productUrlInfoAbout>https://premid.app</productUrlInfoAbout>
|
||||
<readmeFileEncoding>utf-8</readmeFileEncoding>
|
||||
<removeLogFile>1</removeLogFile>
|
||||
<removeUninstallationLogFile>1</removeUninstallationLogFile>
|
||||
<requestedExecutionLevel>asInvoker</requestedExecutionLevel>
|
||||
<saveRelativePaths>1</saveRelativePaths>
|
||||
<summary>Rich Presence for web services.</summary>
|
||||
<unattendedModeUI>minimalWithDialogs</unattendedModeUI>
|
||||
<vendor>Timeraa</vendor>
|
||||
<width>625</width>
|
||||
<windowsExecutableIcon>appIcon.ico</windowsExecutableIcon>
|
||||
<windowsResourceFileDescription>Rich Presence for web services.</windowsResourceFileDescription>
|
||||
<windowsResourceFileVersion>${product_version}</windowsResourceFileVersion>
|
||||
<windowsUninstallerExecutableIcon>appIcon.ico</windowsUninstallerExecutableIcon>
|
||||
<licenseFileList>
|
||||
<licenseFile>
|
||||
<code>en</code>
|
||||
<encoding>utf-8</encoding>
|
||||
<file>../LICENSE</file>
|
||||
</licenseFile>
|
||||
</licenseFileList>
|
||||
<parameterList>
|
||||
<parameterGroup>
|
||||
<name>post_install_page</name>
|
||||
<title>Installation Finished</title>
|
||||
<explanation></explanation>
|
||||
<value></value>
|
||||
<default></default>
|
||||
<insertAfter>installation</insertAfter>
|
||||
<parameterList>
|
||||
<labelParameter>
|
||||
<name>general</name>
|
||||
<description>General</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>addDesktop</name>
|
||||
<description>Create Desktop Icon</description>
|
||||
<explanation></explanation>
|
||||
<value>false</value>
|
||||
<default>false</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<booleanParameter>
|
||||
<name>launchApp</name>
|
||||
<description>Launch App</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<labelParameter>
|
||||
<name>extra</name>
|
||||
<description>Extra</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>openStore</name>
|
||||
<description>Open Presence Store</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
</parameterList>
|
||||
<postShowPageActionList>
|
||||
<createShortcuts>
|
||||
<destination>${windows_folder_desktopdirectory}</destination>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${addDesktop}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
<shortcutList>
|
||||
<quickLaunchShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</quickLaunchShortcut>
|
||||
</shortcutList>
|
||||
</createShortcuts>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>PreMiD.exe</program>
|
||||
<programArguments>&</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<workingDirectory>${installdir}</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>open</program>
|
||||
<programArguments>${installdir}/PreMiD.app</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<launchBrowser>
|
||||
<url>https://premid.app/store</url>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${openStore}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</launchBrowser>
|
||||
<exit/>
|
||||
</postShowPageActionList>
|
||||
</parameterGroup>
|
||||
</parameterList>
|
||||
<platformOptionsList>
|
||||
<platformOptions>
|
||||
<platform>windows</platform>
|
||||
</platformOptions>
|
||||
</platformOptionsList>
|
||||
</project>
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
<project>
|
||||
<shortName>PreMiD</shortName>
|
||||
<fullName>PreMiD</fullName>
|
||||
<version>latest</version>
|
||||
<installerFilename>${product_shortname}-installer.${platform_exec_suffix}</installerFilename>
|
||||
<debugLevel>0</debugLevel>
|
||||
<licenseFile>../LICENSE</licenseFile>
|
||||
<leftImage>leftSide.png</leftImage>
|
||||
<logoImage>logo.png</logoImage>
|
||||
<splashImage>logo.png</splashImage>
|
||||
<componentList>
|
||||
<component>
|
||||
<name>default</name>
|
||||
<description>Default Component</description>
|
||||
<canBeEdited>1</canBeEdited>
|
||||
<selected>1</selected>
|
||||
<show>1</show>
|
||||
<folderList>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfiles</name>
|
||||
<platforms>all</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux</name>
|
||||
<platforms>linux</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux64</name>
|
||||
<platforms>linux-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows</name>
|
||||
<platforms>windows</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows64</name>
|
||||
<platforms>windows-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfilesosx</name>
|
||||
<platforms>osx</platforms>
|
||||
</folder>
|
||||
</folderList>
|
||||
<startMenuShortcutList>
|
||||
<startMenuShortcut>
|
||||
<comment>Uninstall ${product_fullname}</comment>
|
||||
<name>Uninstall ${product_fullname}</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/${uninstallerName}.exe</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath>${installdir}/</windowsPath>
|
||||
</startMenuShortcut>
|
||||
<startMenuShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</startMenuShortcut>
|
||||
</startMenuShortcutList>
|
||||
</component>
|
||||
</componentList>
|
||||
<initializationActionList>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${windows_folder_appdata}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${platform_install_prefix}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
</initializationActionList>
|
||||
<readyToInstallActionList>
|
||||
<actionGroup>
|
||||
<customErrorMessage>Couldn't download release... Try again later.</customErrorMessage>
|
||||
<progressText>Downloading latest release...</progressText>
|
||||
<actionList>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<width>450</width>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x64</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-ia32.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x86</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-darwin-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<actionList>
|
||||
<!-- Remove the old ARP Entry
|
||||
Get the old version -->
|
||||
<registryGet>
|
||||
<key>HKEY_LOCAL_MACHINE\Software\${project.windowsSoftwareRegistryPrefix}</key>
|
||||
<name>Version</name>
|
||||
<variable>oldVersion</variable>
|
||||
</registryGet>
|
||||
|
||||
<!-- Delete the old ARP registry keys -->
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Management\ARPCache\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest type="windows"/>
|
||||
<isTrue value="${isUpgradeMode}"/>
|
||||
</ruleList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</readyToInstallActionList>
|
||||
<postInstallationActionList>
|
||||
<unzip>
|
||||
<addToUninstaller>1</addToUninstaller>
|
||||
<destinationDirectory>${installdir}</destinationDirectory>
|
||||
<progressText>Extracting release...</progressText>
|
||||
<zipFile>${system_temp_directory}/PreMiD-release.zip</zipFile>
|
||||
</unzip>
|
||||
<addDirectoriesToUninstaller>
|
||||
<addContents>1</addContents>
|
||||
<files>${installdir}/</files>
|
||||
</addDirectoriesToUninstaller>
|
||||
</postInstallationActionList>
|
||||
<preUninstallationActionList>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</preUninstallationActionList>
|
||||
<compressionAlgorithm>lzham-ultra</compressionAlgorithm>
|
||||
<createOsxBundleZip>1</createOsxBundleZip>
|
||||
<deleteOnExit>1</deleteOnExit>
|
||||
<description>Rich Presence for web services.</description>
|
||||
<disableSplashScreen>1</disableSplashScreen>
|
||||
<enableRollback>0</enableRollback>
|
||||
<enableSslSupport>1</enableSslSupport>
|
||||
<enableTimestamp>1</enableTimestamp>
|
||||
<installationScope>user</installationScope>
|
||||
<licenseFileEncoding>utf-8</licenseFileEncoding>
|
||||
<osxApplicationBundleIcon>appIcon.icns</osxApplicationBundleIcon>
|
||||
<osxApplicationBundleIdentifier>eu.Timeraa.PreMiD</osxApplicationBundleIdentifier>
|
||||
<osxPlatforms>osx-intel osx-x86_64</osxPlatforms>
|
||||
<osxUninstallerApplicationBundleIcon>appIcon.icns</osxUninstallerApplicationBundleIcon>
|
||||
<outputDirectory>../dist/installer</outputDirectory>
|
||||
<overwritePolicy>onlyIfNewer</overwritePolicy>
|
||||
<productDisplayIcon>C:/Users/metzf/Documents/Development/PreMiD/PreMiD/installer_assets/appIcon.ico</productDisplayIcon>
|
||||
<productUrlHelpLink>https://discord.premid.app</productUrlHelpLink>
|
||||
<productUrlInfoAbout>https://premid.app</productUrlInfoAbout>
|
||||
<readmeFileEncoding>utf-8</readmeFileEncoding>
|
||||
<removeLogFile>1</removeLogFile>
|
||||
<removeUninstallationLogFile>1</removeUninstallationLogFile>
|
||||
<requestedExecutionLevel>asInvoker</requestedExecutionLevel>
|
||||
<saveRelativePaths>1</saveRelativePaths>
|
||||
<summary>Rich Presence for web services.</summary>
|
||||
<vendor>Timeraa</vendor>
|
||||
<windowsExecutableIcon>appIcon.ico</windowsExecutableIcon>
|
||||
<windowsResourceFileDescription>Rich Presence for web services.</windowsResourceFileDescription>
|
||||
<windowsResourceFileVersion>${product_version}</windowsResourceFileVersion>
|
||||
<windowsUninstallerExecutableIcon>appIcon.ico</windowsUninstallerExecutableIcon>
|
||||
<licenseFileList>
|
||||
<licenseFile>
|
||||
<code>en</code>
|
||||
<encoding>utf-8</encoding>
|
||||
<file>../LICENSE</file>
|
||||
</licenseFile>
|
||||
</licenseFileList>
|
||||
<parameterList>
|
||||
<parameterGroup>
|
||||
<name>post_install_page</name>
|
||||
<title>Installation Finished</title>
|
||||
<explanation></explanation>
|
||||
<value></value>
|
||||
<default></default>
|
||||
<insertAfter>installation</insertAfter>
|
||||
<parameterList>
|
||||
<labelParameter>
|
||||
<name>general</name>
|
||||
<description>General</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>addDesktop</name>
|
||||
<description>Create Desktop Icon</description>
|
||||
<explanation></explanation>
|
||||
<value>false</value>
|
||||
<default>false</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<booleanParameter>
|
||||
<name>launchApp</name>
|
||||
<description>Launch App</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<labelParameter>
|
||||
<name>extra</name>
|
||||
<description>Extra</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>openStore</name>
|
||||
<description>Open Presence Store</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
</parameterList>
|
||||
<postShowPageActionList>
|
||||
<createShortcuts>
|
||||
<destination>${windows_folder_desktopdirectory}</destination>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${addDesktop}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
<shortcutList>
|
||||
<quickLaunchShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</quickLaunchShortcut>
|
||||
</shortcutList>
|
||||
</createShortcuts>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>PreMiD.exe</program>
|
||||
<programArguments>&</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<workingDirectory>${installdir}</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>open</program>
|
||||
<programArguments>${installdir}/PreMiD.app</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<launchBrowser>
|
||||
<url>https://premid.app/store</url>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${openStore}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</launchBrowser>
|
||||
<exit/>
|
||||
</postShowPageActionList>
|
||||
</parameterGroup>
|
||||
</parameterList>
|
||||
<platformOptionsList>
|
||||
<platformOptions>
|
||||
<platform>windows</platform>
|
||||
</platformOptions>
|
||||
</platformOptionsList>
|
||||
</project>
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
76
package.json
@@ -1,56 +1,38 @@
|
||||
{
|
||||
"name": "premid",
|
||||
"productName": "PreMiD",
|
||||
"description": "Discord Rich Presence for web services.",
|
||||
"version": "2.2.1",
|
||||
"repository": "https://github.com/PreMiD/PreMiD",
|
||||
"version": "1.0.0",
|
||||
"description": "Monorepo containing most of PreMiD's codebase",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"init": "tsc --skipLibCheck && tsc pkg util/prepare util/zip && devScript --copyOnly",
|
||||
"start": "electron dist/app/. --unhandled-rejections=strict",
|
||||
"dev": "devscript --depCheck",
|
||||
"pkg": "rimraf dist && tsc --skipLibCheck && devScript --copyOnly && cd dist/app/ && yarn && cd ../../ && node pkg",
|
||||
"deploy": "tsc --skipLibCheck .github/deploy && cd .github && node deploy.js"
|
||||
"prepare": "husky",
|
||||
"lint": "eslint . --ext .ts,.js && prettier --check .",
|
||||
"lint:fix": "eslint . --ext .ts,.js --fix && prettier --write .",
|
||||
"build": "pnpm clean && tsc -b tsconfig.app.json",
|
||||
"build:watch": "pnpm clean && tsc -b tsconfig.app.json -w",
|
||||
"clean": "tsc -b tsconfig.app.json --clean",
|
||||
"test": "vitest --run",
|
||||
"test:ui": "vitest --ui"
|
||||
},
|
||||
"author": {
|
||||
"name": "Recodive oHG",
|
||||
"email": "contact@recodive.com",
|
||||
"url": "https://recodive.com"
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/archiver": "5.1.0",
|
||||
"@types/auto-launch": "5.0.1",
|
||||
"@types/discord-rpc": "3.0.4",
|
||||
"@types/fs-extra": "9.0.11",
|
||||
"@types/ini": "1.3.30",
|
||||
"@types/node": "14.14.41",
|
||||
"@types/prompts": "2.0.11",
|
||||
"@types/request-promise-native": "1.0.17",
|
||||
"@types/rimraf": "3.0.0",
|
||||
"@types/socket.io": "2.1.13",
|
||||
"@types/ssh2-sftp-client": "5.3.1",
|
||||
"@types/unzipper": "^0.10.3",
|
||||
"archiver": "5.2.0",
|
||||
"chalk": "4.1.1",
|
||||
"electron": "11.3.0",
|
||||
"electron-packager": "15.2.0",
|
||||
"fast-glob": "3.2.5",
|
||||
"fs-extra": "9.1.0",
|
||||
"nodemon": "2.0.7",
|
||||
"ora": "5.3.0",
|
||||
"prompts": "2.4.1",
|
||||
"rimraf": "3.0.2",
|
||||
"ssh2-sftp-client": "6.0.1",
|
||||
"ts-devscript": "^3.0.3",
|
||||
"typescript": "4.1.5",
|
||||
"unzipper": "0.10.11",
|
||||
"yarn": "1.22.10"
|
||||
"@recodive/configs": "^1.7.1",
|
||||
"@recodive/eslint-config": "^1.7.1",
|
||||
"@rushstack/eslint-patch": "^1.7.2",
|
||||
"@vitest/coverage-v8": "^1.2.2",
|
||||
"@vitest/ui": "^1.2.2",
|
||||
"eslint": "^8.56.0",
|
||||
"husky": "^9.0.10",
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"auto-launch": "5.0.5",
|
||||
"axios": "0.21.1",
|
||||
"chokidar": "3.5.1",
|
||||
"discord-rpc": "github:discordjs/RPC",
|
||||
"electron-store": "7.0.3",
|
||||
"socket.io": "3.1.2",
|
||||
"source-map-support": "^0.5.19"
|
||||
},
|
||||
"devScript": {
|
||||
"out": "dist/app"
|
||||
"packageManager": "pnpm@8.14.3",
|
||||
"engines": {
|
||||
"node": "^20.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
123
pkg.ts
@@ -1,123 +0,0 @@
|
||||
import * as electronPackager from "electron-packager";
|
||||
import { platform } from "os";
|
||||
import * as prompts from "prompts";
|
||||
import * as ora from "ora";
|
||||
|
||||
(async () => {
|
||||
let response = {
|
||||
os: "current",
|
||||
arch: "all"
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== "DePloY")
|
||||
response = await prompts([
|
||||
{
|
||||
type: "select",
|
||||
name: "arch",
|
||||
message: "What architecture?",
|
||||
choices: [
|
||||
{
|
||||
title: "current",
|
||||
value: "current"
|
||||
},
|
||||
{
|
||||
title: "all",
|
||||
value: "all"
|
||||
},
|
||||
{
|
||||
title: "arm64",
|
||||
value: "arm64"
|
||||
},
|
||||
{
|
||||
title: "armv7l",
|
||||
value: "armv7l"
|
||||
},
|
||||
{
|
||||
title: "ia32",
|
||||
value: "ia32"
|
||||
},
|
||||
{
|
||||
title: "mips64el",
|
||||
value: "mips64el"
|
||||
},
|
||||
{
|
||||
title: "x64",
|
||||
value: "x64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
name: "os",
|
||||
message: "What operating system?",
|
||||
choices: [
|
||||
{
|
||||
title: "current",
|
||||
value: "current"
|
||||
},
|
||||
{
|
||||
title: "all",
|
||||
value: "all"
|
||||
},
|
||||
{
|
||||
title: "darwin",
|
||||
value: "darwin"
|
||||
},
|
||||
{
|
||||
title: "linux",
|
||||
value: "linux"
|
||||
},
|
||||
{
|
||||
title: "mas",
|
||||
value: "mas"
|
||||
},
|
||||
{
|
||||
title: "win32",
|
||||
value: "win32"
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
if (!response.os) {
|
||||
process.exit();
|
||||
}
|
||||
|
||||
let icon: string;
|
||||
|
||||
if (
|
||||
response.os == "darwin" ||
|
||||
(response.os === "current" && platform() === "darwin")
|
||||
)
|
||||
icon = "./installer_assets/appIcon.icns";
|
||||
if (["ia32", "x64"].includes(response.arch) || platform() === "win32")
|
||||
icon = "./installer_assets/appIcon.ico";
|
||||
|
||||
let spinner = ora("Packaging app").start(),
|
||||
packagingOptions: electronPackager.Options = {
|
||||
dir: "./dist/app",
|
||||
out: "./dist",
|
||||
darwinDarkModeSupport: true,
|
||||
icon: icon,
|
||||
overwrite: true,
|
||||
quiet: true,
|
||||
appBundleId: "eu.Timeraa.PreMiD",
|
||||
appCategoryType: "Utilities",
|
||||
appCopyright: `Timeraa 2018-${new Date().getFullYear()}`,
|
||||
prune: true,
|
||||
asar: true,
|
||||
// @ts-ignore
|
||||
arch: response.arch,
|
||||
// @ts-ignore
|
||||
platform: response.os
|
||||
};
|
||||
|
||||
if (response.arch === "current") delete packagingOptions.arch;
|
||||
if (response.os === "current") delete packagingOptions.platform;
|
||||
|
||||
electronPackager(packagingOptions).then(() => {
|
||||
spinner.text = "Done!";
|
||||
spinner.succeed();
|
||||
process.exit();
|
||||
});
|
||||
})();
|
||||
4085
pnpm-lock.yaml
generated
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- apps/*
|
||||
|
Before Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 445 B |
|
Before Width: | Height: | Size: 481 B |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 688 B |
|
Before Width: | Height: | Size: 858 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 880 B |
|
Before Width: | Height: | Size: 986 B |
|
Before Width: | Height: | Size: 343 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Слой_1" data-name="Слой 1" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#fff;fill-rule:evenodd}</style></defs><title>pmd_logo_1</title><path d="M459.17,105H52.83a30.35,30.35,0,0,0-30.34,30.34V376.66A30.35,30.35,0,0,0,52.83,407H459.17a30.35,30.35,0,0,0,30.34-30.34V135.34A30.35,30.35,0,0,0,459.17,105Zm-185,94.4a13.93,13.93,0,0,1,9.9-4.11h90.29a14,14,0,0,1,0,28H284a14,14,0,0,1-9.9-23.91Zm-63.64,63.11L92.14,330.83a7.51,7.51,0,0,1-11.27-6.51V187.68a7.51,7.51,0,0,1,11.27-6.51l118.34,68.32A7.52,7.52,0,0,1,210.48,262.51ZM441.81,312.6a13.93,13.93,0,0,1-9.9,4.11H284a14,14,0,0,1,0-28H431.91a14,14,0,0,1,9.9,23.91Zm0-46.7a13.93,13.93,0,0,1-9.9,4.11H284a14,14,0,0,1,0-28H431.91a14,14,0,0,1,9.9,23.91Z" class="cls-1"/></svg>
|
||||
|
Before Width: | Height: | Size: 771 B |
34
src/index.ts
@@ -1,34 +0,0 @@
|
||||
import "source-map-support/register";
|
||||
|
||||
import { app } from "electron";
|
||||
import { platform } from "os";
|
||||
|
||||
import { update as initAutoLaunch } from "./managers/launchManager";
|
||||
import { init as initSocket } from "./managers/socketManager";
|
||||
import { TrayManager } from "./managers/trayManager";
|
||||
import { checkForUpdate } from "./util/updateChecker";
|
||||
|
||||
export let trayManager: TrayManager;
|
||||
|
||||
//* Define and set it to null
|
||||
//* Set AppUserModelId for task manager etc
|
||||
//* When app is ready
|
||||
export let updateCheckerInterval = null;
|
||||
|
||||
//* Attempt to get lock to prevent multiple instances of PreMiD from running
|
||||
let singleInstanceLock = app.requestSingleInstanceLock();
|
||||
|
||||
//* Application already running?
|
||||
if (!singleInstanceLock) app.quit();
|
||||
|
||||
app.setAppUserModelId("Timeraa.PreMiD");
|
||||
app.whenReady().then(async () => {
|
||||
trayManager = new TrayManager();
|
||||
|
||||
await Promise.all([checkForUpdate(true), initAutoLaunch(), initSocket()]);
|
||||
|
||||
app.isPackaged
|
||||
? (updateCheckerInterval = setInterval(checkForUpdate, 15 * 1000 * 60))
|
||||
: undefined;
|
||||
if (platform() === "darwin") app.dock.hide();
|
||||
});
|
||||
@@ -1,123 +0,0 @@
|
||||
import { Client } from "discord-rpc";
|
||||
import { app } from "electron";
|
||||
|
||||
import { trayManager } from "../";
|
||||
//* Import custom types
|
||||
import PresenceData from "../../@types/PreMiD/PresenceData";
|
||||
import { info } from "../util/debug";
|
||||
|
||||
export let rpcClients: Array<RPCClient> = [];
|
||||
|
||||
class RPCClient {
|
||||
clientId: string;
|
||||
currentPresence: PresenceData;
|
||||
client: Client;
|
||||
clientReady: boolean = false;
|
||||
|
||||
constructor(clientId: string) {
|
||||
rpcClients.push(this);
|
||||
|
||||
this.clientId = clientId;
|
||||
this.client = new Client({
|
||||
transport: "ipc"
|
||||
});
|
||||
|
||||
this.client.once("ready", () => {
|
||||
this.clientReady = true;
|
||||
this.setActivity();
|
||||
});
|
||||
this.client.once(
|
||||
// @ts-ignore
|
||||
"disconnected",
|
||||
() =>
|
||||
(rpcClients = rpcClients.filter(
|
||||
client => client.clientId !== this.clientId
|
||||
))
|
||||
);
|
||||
|
||||
this.client.login({ clientId: this.clientId }).catch(() => this.destroy());
|
||||
|
||||
info(`Create RPC client (${this.clientId})`);
|
||||
}
|
||||
|
||||
setActivity(presenceData?: PresenceData) {
|
||||
presenceData = presenceData ? presenceData : this.currentPresence;
|
||||
|
||||
if (!this.clientReady || !presenceData) return;
|
||||
|
||||
if (presenceData.trayTitle)
|
||||
trayManager.tray.setTitle(presenceData.trayTitle);
|
||||
|
||||
this.client
|
||||
.setActivity(presenceData.presenceData)
|
||||
.catch(() => this.destroy());
|
||||
info("setActivity");
|
||||
}
|
||||
|
||||
clearActivity() {
|
||||
this.currentPresence = null;
|
||||
|
||||
if (!this.clientReady) return;
|
||||
|
||||
this.client.clearActivity().catch(() => this.destroy());
|
||||
trayManager.tray.setTitle("");
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
try {
|
||||
info(`Destroy RPC client (${this.clientId})`);
|
||||
if (this.clientReady) {
|
||||
this.client.clearActivity();
|
||||
this.client.destroy();
|
||||
}
|
||||
|
||||
trayManager.tray.setTitle("");
|
||||
rpcClients = rpcClients.filter(
|
||||
client => client.clientId !== this.clientId
|
||||
);
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's activity
|
||||
* @param presence PresenceData to set activity
|
||||
*/
|
||||
export function setActivity(presence: PresenceData) {
|
||||
let client = rpcClients.find(c => c.clientId === presence.clientId);
|
||||
|
||||
if (!client) {
|
||||
client = new RPCClient(presence.clientId);
|
||||
client.currentPresence = presence;
|
||||
} else client.setActivity(presence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a user's activity
|
||||
* @param clientId clientId of presence to clear
|
||||
*/
|
||||
export function clearActivity(clientId: string = undefined) {
|
||||
info("clearActivity");
|
||||
|
||||
if (clientId) {
|
||||
let client = rpcClients.find(c => c.clientId === clientId);
|
||||
client.clearActivity();
|
||||
} else rpcClients.forEach(c => c.clearActivity());
|
||||
}
|
||||
|
||||
export async function getDiscordUser() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const c = new Client({ transport: "ipc" });
|
||||
|
||||
c.login({
|
||||
clientId: "503557087041683458"
|
||||
})
|
||||
.then(({ user }) => c.destroy().then(() => resolve(user)))
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
app.once(
|
||||
"will-quit",
|
||||
async () => await Promise.all(rpcClients.map(c => c.destroy()))
|
||||
);
|
||||
@@ -1,29 +0,0 @@
|
||||
import AutoLaunch from "auto-launch";
|
||||
import { app } from "electron";
|
||||
import { settings } from "./settingsManager";
|
||||
import { info } from "../util/debug";
|
||||
|
||||
//* Create autoLaunch object
|
||||
let autoLaunch = new AutoLaunch({
|
||||
name: app.name,
|
||||
isHidden: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Updates autoLaunch
|
||||
*/
|
||||
export async function update() {
|
||||
//* If app not packaged return
|
||||
//* Either enable/disable autolaunch
|
||||
if (!app.isPackaged) {
|
||||
//* Show debug
|
||||
//* Return
|
||||
info("Skipping autoLaunch.");
|
||||
return;
|
||||
}
|
||||
if (settings.get("autoLaunch", true))
|
||||
//* Enable if not enabled
|
||||
autoLaunch.enable();
|
||||
//* Disable if enabled
|
||||
else autoLaunch.disable();
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { readdirSync, readFileSync, unwatchFile } from "fs";
|
||||
import { dialog, app } from "electron";
|
||||
import { socket } from "./socketManager";
|
||||
import { extname } from "path";
|
||||
import { info } from "../util/debug";
|
||||
|
||||
import chokidar from "chokidar";
|
||||
|
||||
let presenceDevWatchedFiles = [],
|
||||
currWatchPath = "",
|
||||
currWatcher: chokidar.FSWatcher = null;
|
||||
|
||||
export async function watchDir(path: string) {
|
||||
currWatchPath = path + "/";
|
||||
let files = readdirSync(path);
|
||||
|
||||
if (currWatcher) await currWatcher.close();
|
||||
|
||||
currWatcher = chokidar.watch(currWatchPath, {
|
||||
ignoreInitial: true,
|
||||
ignored: ["*.ts"]
|
||||
});
|
||||
|
||||
currWatcher.on("all", eventName => {
|
||||
files = readdirSync(currWatchPath);
|
||||
|
||||
console.log(eventName, currWatchPath, files);
|
||||
|
||||
readFiles(files, currWatchPath);
|
||||
});
|
||||
|
||||
readFiles(files, path);
|
||||
}
|
||||
|
||||
async function readFiles(files, path) {
|
||||
//* Send files to extension
|
||||
socket.emit("localPresence", {
|
||||
files: await Promise.all(
|
||||
files.map(f => {
|
||||
if (extname(f) === ".json")
|
||||
return {
|
||||
file: f,
|
||||
contents: JSON.parse(readFileSync(`${path}/${f}`).toString())
|
||||
};
|
||||
else if (extname(f) === ".js")
|
||||
return {
|
||||
file: f,
|
||||
contents: readFileSync(`${path}/${f}`).toString()
|
||||
};
|
||||
else return;
|
||||
})
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
export async function openFileDialog() {
|
||||
//* Open file dialog
|
||||
//* If user cancels
|
||||
//* Unwatch all still watched files
|
||||
//* Watch directory
|
||||
app.focus();
|
||||
let oDialog = await dialog.showOpenDialog(null, {
|
||||
title: "Select Presence Folder",
|
||||
message:
|
||||
"Please select the folder that contains the presence you want to load.\n(metadata.json, presence.js, iframe.js)",
|
||||
buttonLabel: "Load Presence",
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
if (oDialog.canceled) {
|
||||
//* Show debug
|
||||
//* return
|
||||
info("Presence load canceled.");
|
||||
return;
|
||||
}
|
||||
info(`Watching ${oDialog.filePaths[0]}`);
|
||||
if (presenceDevWatchedFiles.length > 0)
|
||||
await Promise.all(
|
||||
presenceDevWatchedFiles.map(f => unwatchFile(currWatchPath + f))
|
||||
);
|
||||
|
||||
watchDir(oDialog.filePaths[0]);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import ElectronStore from "electron-store";
|
||||
import { update as updateAutoLaunch } from "./launchManager";
|
||||
import { platform } from "os";
|
||||
import { info } from "../util/debug";
|
||||
|
||||
//* Import custom types
|
||||
import ExtensionSettings from "../../@types/PreMiD/ExtensionSettings";
|
||||
import { trayManager } from "..";
|
||||
|
||||
//* Export and set default settings
|
||||
export let settings = new ElectronStore({
|
||||
defaults: {
|
||||
autoLaunch: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Update settings of app
|
||||
* @param extensionSettings Settings from extension
|
||||
*/
|
||||
export function update(extensionSettings: ExtensionSettings) {
|
||||
//* Show debug
|
||||
//* remove title if disabled
|
||||
//* Update autolaunch if updated
|
||||
//* Save Settings
|
||||
info("Updated settings");
|
||||
if (!extensionSettings.titleMenubar && platform() === "darwin")
|
||||
trayManager.tray.setTitle("");
|
||||
if (settings.get("autoLaunch") != extensionSettings.autoLaunch) {
|
||||
settings.set("autoLaunch", extensionSettings.autoLaunch);
|
||||
updateAutoLaunch();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { app, dialog } from "electron";
|
||||
import { createServer, Server } from "http";
|
||||
import socketIo from "socket.io";
|
||||
|
||||
import { trayManager } from "../";
|
||||
import { error, success } from "../util/debug";
|
||||
import {
|
||||
clearActivity,
|
||||
getDiscordUser,
|
||||
rpcClients,
|
||||
setActivity
|
||||
} from "./discordManager";
|
||||
import { openFileDialog } from "./presenceDevManager";
|
||||
import { update as updateSettings } from "./settingsManager";
|
||||
|
||||
export let io: socketIo.Server;
|
||||
export let socket: socketIo.Socket;
|
||||
export let server: Server;
|
||||
export let connected: boolean = false;
|
||||
|
||||
export function init() {
|
||||
return new Promise<void>(resolve => {
|
||||
//* Create server
|
||||
//* create SocketIo server, don't server client
|
||||
//* Try to listen to port 3020
|
||||
//* If that fails/some other error happens run socketError
|
||||
//* If someone connects to socket socketConnection
|
||||
server = createServer();
|
||||
io = new socketIo.Server(server, {
|
||||
serveClient: false,
|
||||
allowEIO3: true,
|
||||
allowRequest: (req, callback) => {
|
||||
if (req.headers.origin === undefined) return callback(null, true);
|
||||
//* 2.5.0+ only allows extensions to connect
|
||||
if (
|
||||
req.headers.origin.startsWith("chrome-extension://") ||
|
||||
req.headers.origin.startsWith("moz-extension://")
|
||||
)
|
||||
return callback(null, true);
|
||||
|
||||
callback("Origin not allowed", false);
|
||||
}
|
||||
});
|
||||
server.listen(3020, () => {
|
||||
//* Resolve promise
|
||||
//* Debug info
|
||||
resolve();
|
||||
success("Opened socket");
|
||||
});
|
||||
server.on("error", socketError);
|
||||
io.on("connection", socketConnection);
|
||||
});
|
||||
}
|
||||
|
||||
function socketConnection(cSocket: socketIo.Socket) {
|
||||
//* Show debug
|
||||
//* Set exported socket letiable to current socket
|
||||
//* Handle setActivity event
|
||||
//* Handle clearActivity event
|
||||
//* Handle settingsUpdate
|
||||
//* Handle presenceDev
|
||||
//* Handle version request
|
||||
//* Once socket user disconnects run cleanup
|
||||
success("Socket connection");
|
||||
socket = cSocket;
|
||||
getDiscordUser()
|
||||
.then(user => socket.emit("discordUser", user))
|
||||
.catch(_ => socket.emit("discordUser", null));
|
||||
socket.on("setActivity", setActivity);
|
||||
socket.on("clearActivity", clearActivity);
|
||||
socket.on("settingUpdate", updateSettings);
|
||||
socket.on("selectLocalPresence", openFileDialog);
|
||||
socket.on("getVersion", () =>
|
||||
socket.emit("receiveVersion", app.getVersion().replace(/[\D]/g, ""))
|
||||
);
|
||||
socket.once("disconnect", () => {
|
||||
connected = false;
|
||||
trayManager.update();
|
||||
//* Show debug
|
||||
//* Destroy all open RPC connections
|
||||
error("Socket disconnection.");
|
||||
rpcClients.forEach(c => c.destroy());
|
||||
});
|
||||
connected = true;
|
||||
trayManager.update();
|
||||
}
|
||||
|
||||
//* Runs on socket errors
|
||||
function socketError(e: any) {
|
||||
//* Show debug
|
||||
//* If port in use
|
||||
error(e.message);
|
||||
if (e.code === "EADDRINUSE") {
|
||||
//* Focus app
|
||||
//* Show error dialog
|
||||
//* Exit app afterwards
|
||||
app.focus();
|
||||
dialog.showErrorBox(
|
||||
"Oh noes! Port error...",
|
||||
`${app.name} could not bind to port ${e.port}.\nIs ${app.name} running already?`
|
||||
);
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { app, Menu, shell, Tray } from "electron";
|
||||
import { platform } from "os";
|
||||
import { join } from "path";
|
||||
|
||||
import { trayManager } from "../";
|
||||
import { checkForUpdate, update, updateAvailable } from "../util/updateChecker";
|
||||
import { connected } from "./socketManager";
|
||||
|
||||
let trayIcon;
|
||||
|
||||
switch (platform()) {
|
||||
case "darwin":
|
||||
trayIcon = join(__dirname, "../assets/tray/IconTemplate.png");
|
||||
break;
|
||||
case "win32":
|
||||
trayIcon = join(__dirname, "../assets/tray/Icon.ico");
|
||||
break;
|
||||
default:
|
||||
trayIcon = join(__dirname, "../assets/tray/Icon@4x.png");
|
||||
break;
|
||||
}
|
||||
|
||||
export class TrayManager {
|
||||
tray: Tray;
|
||||
|
||||
constructor() {
|
||||
this.tray = new Tray(trayIcon);
|
||||
this.tray.setToolTip(app.name);
|
||||
|
||||
this.tray.on("right-click", () => this.update());
|
||||
}
|
||||
|
||||
update() {
|
||||
this.tray.setContextMenu(
|
||||
Menu.buildFromTemplate([
|
||||
{
|
||||
icon:
|
||||
platform() === "darwin"
|
||||
? join(__dirname, "../assets/tray/IconTemplate.png")
|
||||
: join(__dirname, "../assets/tray/Icon@4x.png"),
|
||||
label: `${app.name} v${app.getVersion()}`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: "connectInfo",
|
||||
label: `Extension - ${connected ? "Connected" : "Not connected"}`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: "Presence Store",
|
||||
click: () => shell.openExternal("https://premid.app/store")
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: `Update ${app.name}!`,
|
||||
visible: updateAvailable,
|
||||
click: () => update()
|
||||
},
|
||||
{
|
||||
label: "Check for Updates",
|
||||
click: () => checkForUpdate(false, true),
|
||||
visible: !updateAvailable
|
||||
},
|
||||
{
|
||||
label: "Contributors",
|
||||
click: () => shell.openExternal("https://premid.app/contributors")
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: `Quit ${app.name}`,
|
||||
role: "quit"
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
app.once("quit", () => trayManager?.tray.destroy());
|
||||
@@ -1,32 +0,0 @@
|
||||
import { app } from "electron";
|
||||
if (!app.isPackaged) var chalk = require("chalk");
|
||||
|
||||
/**
|
||||
* Show info message in console
|
||||
* */
|
||||
export function info(message: string) {
|
||||
//* Return if app packaged
|
||||
//* Show debug
|
||||
if (app.isPackaged) return;
|
||||
console.log(`${chalk.bgBlue(chalk.white(" INFO "))} ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show success message in console
|
||||
* */
|
||||
export function success(message: string) {
|
||||
//* Return if app packaged
|
||||
//* Show debug
|
||||
if (app.isPackaged) return;
|
||||
console.log(`${chalk.bgGreen(" SUCCESS ")} ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message in console
|
||||
* */
|
||||
export function error(message: string) {
|
||||
//* Return if app packaged
|
||||
//* Show debug
|
||||
if (app.isPackaged) return;
|
||||
console.log(`${chalk.bgRed(" ERROR ")} ${message}`);
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import { exec } from "child_process";
|
||||
import { resolve } from "path";
|
||||
import { error, info } from "./debug";
|
||||
import { app, dialog, shell } from "electron";
|
||||
import { platform } from "os";
|
||||
import axios from "axios";
|
||||
import { Notification } from "electron";
|
||||
import { trayManager } from "..";
|
||||
import { createWriteStream, existsSync, unlinkSync } from "fs";
|
||||
|
||||
export let updateAvailable = false;
|
||||
let initialNotification = true;
|
||||
|
||||
export async function checkForUpdate(autoUpdate = false, manual = false) {
|
||||
//* Skip Update checker if unsupported OS / not packaged
|
||||
if (!app.isPackaged || !["darwin", "win32"].includes(platform())) {
|
||||
//* Show debug
|
||||
//* return
|
||||
info("Skipping UpdateChecker");
|
||||
return;
|
||||
}
|
||||
|
||||
axios
|
||||
.get("https://api.premid.app/v2/versions")
|
||||
.then(({ data }) => {
|
||||
if (!data?.app) return;
|
||||
|
||||
const latestAppVersion = data.app;
|
||||
|
||||
if (app.getVersion() >= latestAppVersion) {
|
||||
if (manual)
|
||||
dialog.showMessageBox(null, {
|
||||
message: "There are currently no updates available.",
|
||||
type: "info"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoUpdate) {
|
||||
updateTray();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialNotification) {
|
||||
const updateNotification = new Notification({
|
||||
title: "Update available!",
|
||||
body: "A new version of PreMiD is available! Click here to update."
|
||||
});
|
||||
|
||||
updateNotification.once("click", update);
|
||||
updateNotification.show();
|
||||
updateTray();
|
||||
|
||||
initialNotification = false;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
export async function update() {
|
||||
if (!["win32", "darwin"].includes(platform())) return;
|
||||
const updaterPath = resolve(
|
||||
app.getPath("temp"),
|
||||
`PreMiD-Updater${
|
||||
platform() === "win32" ? ".exe" : ".app/Contents/MacOS/installbuilder.sh"
|
||||
}`
|
||||
);
|
||||
|
||||
if (existsSync(updaterPath)) unlinkSync(updaterPath);
|
||||
if (existsSync(resolve(app.getPath("temp"), "PreMiD-release.zip")))
|
||||
unlinkSync(resolve(app.getPath("temp"), "PreMiD-release.zip"));
|
||||
|
||||
const response = await axios({
|
||||
url: `http://dl.premid.app/upgrader${
|
||||
platform() === "win32" ? ".exe" : ".app"
|
||||
}`,
|
||||
method: "GET",
|
||||
responseType: "stream"
|
||||
});
|
||||
|
||||
const writer = createWriteStream(updaterPath);
|
||||
response.data.pipe(writer);
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
writer.on("finish", resolve);
|
||||
writer.on("error", reject);
|
||||
})
|
||||
.then(() => exec(updaterPath, errorHandler))
|
||||
.catch(errorHandler);
|
||||
}
|
||||
|
||||
function errorHandler(err: Error) {
|
||||
dialog
|
||||
.showMessageBox({
|
||||
title: `Error while updating ${app.name}`,
|
||||
message: `If this error persists reinstall the app.`,
|
||||
detail: err.message,
|
||||
buttons: ["Okay", "Reinstall Application"],
|
||||
type: "error"
|
||||
})
|
||||
.then(value => {
|
||||
if (value.response === 1)
|
||||
shell.openExternal("https://premid.app/downloads");
|
||||
});
|
||||
updateTray();
|
||||
}
|
||||
|
||||
function updateTray() {
|
||||
updateAvailable = true;
|
||||
trayManager.update();
|
||||
}
|
||||
8
tsconfig.app.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./apps/pd/tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
34
tsconfig.base.json
Normal file
@@ -0,0 +1,34 @@
|
||||
// Credit: https://www.totaltypescript.com/tsconfig-cheat-sheet
|
||||
{
|
||||
"exclude": ["**/*/node_modules", "**/*/dist"],
|
||||
"compilerOptions": {
|
||||
/* Base Options: */
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2022",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleDetection": "force",
|
||||
"isolatedModules": true,
|
||||
|
||||
/* Strictness */
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
|
||||
/* If transpiling with TypeScript: */
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"outDir": "dist",
|
||||
"sourceMap": true,
|
||||
|
||||
/* AND if you're building for a library: */
|
||||
"declaration": true,
|
||||
|
||||
/* AND if you're building for a library in a monorepo: */
|
||||
"composite": true,
|
||||
"declarationMap": true,
|
||||
|
||||
/* If your code doesn't run in the DOM: */
|
||||
"lib": ["es2022"]
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"moduleResolution": "node",
|
||||
"inlineSourceMap": true,
|
||||
"outDir": "dist/app",
|
||||
"removeComments": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": [
|
||||
"**/node_modules/",
|
||||
"pkg.ts",
|
||||
"util/**/*",
|
||||
"installer_assets/builder.ts",
|
||||
".github/uploadFile.ts"
|
||||
]
|
||||
"noEmit": true,
|
||||
"types": ["vitest"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
|
||||
let file = readFileSync("installer_assets/PreMiD-Upgrade.xml", "utf-8");
|
||||
|
||||
file = file.replace("VERSION", require("../package.json").version);
|
||||
|
||||
writeFileSync("installer_assets/PreMiD-Upgrade.xml", file);
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as Client from "ssh2-sftp-client";
|
||||
|
||||
let sftp = new Client();
|
||||
|
||||
sftp
|
||||
.connect({
|
||||
host: process.env.SSHHOST,
|
||||
username: process.env.SSH_USERNAME,
|
||||
privateKey: process.env.SSH_KEY
|
||||
})
|
||||
.then(async () => {
|
||||
sftp
|
||||
.fastPut(process.argv[2], process.argv[3])
|
||||
.then(() => {
|
||||
sftp.end();
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
16
util/zip.ts
@@ -1,16 +0,0 @@
|
||||
import * as archiver from "archiver";
|
||||
import { Extract } from "unzipper";
|
||||
import { createWriteStream, createReadStream } from "fs";
|
||||
import { basename } from "path";
|
||||
|
||||
if (process.argv.includes("--zip")) {
|
||||
const archive = archiver("zip"),
|
||||
output = createWriteStream(process.argv[3]);
|
||||
|
||||
archive.pipe(output);
|
||||
archive.directory(process.argv[2], basename(process.argv[3], ".zip"));
|
||||
|
||||
archive.finalize();
|
||||
} else {
|
||||
createReadStream(process.argv[2]).pipe(Extract({ path: process.cwd() }));
|
||||
}
|
||||
16
vitest.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
coverage: {
|
||||
all: true,
|
||||
enabled: true,
|
||||
reportOnFailure: true,
|
||||
thresholds: {
|
||||
100: true,
|
||||
},
|
||||
},
|
||||
isolate: false,
|
||||
passWithNoTests: true,
|
||||
},
|
||||
});
|
||||
3
vitest.workspace.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { defineWorkspace } from "vitest/config";
|
||||
|
||||
export default defineWorkspace(["apps/*"]);
|
||||