mirror of
https://github.com/KaitlynEthylia/Disco
synced 2024-11-10 06:34:23 +00:00
Changes for 0.2.0
This commit is contained in:
parent
2c4fcd7bc0
commit
f885ab99c5
15 changed files with 580 additions and 131 deletions
13
.editorconfig
Normal file
13
.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
|
||||
[*.md]
|
||||
max_line_length = 70
|
38
CHANGELOG.md
Normal file
38
CHANGELOG.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# 0.2.0
|
||||
|
||||
## Added
|
||||
|
||||
- Lua values may now be set to functions that will return the desired
|
||||
type. This prevents code being run multiple times when multiple Lua
|
||||
VMs are spawned.
|
||||
|
||||
- The `watch` function has been added as a builtin helper function
|
||||
for executing a shell command and following the output. The
|
||||
function takes a shell command as the first arg, and optionally a
|
||||
value to use if the command fails to start.
|
||||
|
||||
- When built with the `unsafe` feature flag, the `--safe` flag can be
|
||||
passed to the command line to only load safe libraries anyway.
|
||||
|
||||
- The `--dry-run` command line flag can be passed to prevent
|
||||
attempting to connect to Discord.
|
||||
|
||||
- Fish and Zsh completions are now provided with the release.
|
||||
|
||||
- Systemd, Runit, and OpenRC template services can be found in the
|
||||
[etc](/etc) directory.
|
||||
|
||||
## Changed
|
||||
|
||||
- ClientID has been renamed to ApplicationID to better reflect
|
||||
Discord's own usage and reduce ambiguity.
|
||||
|
||||
- Output has been cleaned up, making use of
|
||||
[simplelog](https://lib.rs/simplelog).
|
||||
|
||||
- Command help text has been improved.
|
||||
|
||||
## Fixed
|
||||
|
||||
- The output binary is now named `disco`. Previously it was
|
||||
mistakenly called `disco-rpc` by default.
|
130
Cargo.lock
generated
130
Cargo.lock
generated
|
@ -43,9 +43,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
|
@ -65,9 +65,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.3.3"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
|
@ -95,9 +95,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.19"
|
||||
version = "4.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
|
||||
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -106,9 +106,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.19"
|
||||
version = "4.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
|
||||
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -140,6 +140,12 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
|
@ -162,13 +168,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "disco"
|
||||
version = "1.0.0"
|
||||
name = "disco-rpc"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"discord-rich-presence",
|
||||
"log",
|
||||
"mlua",
|
||||
"simplelog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -256,6 +264,12 @@ version = "0.4.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -285,6 +299,15 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
|
@ -349,11 +372,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.6"
|
||||
version = "0.38.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
@ -394,6 +417,17 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acee08041c5de3d5048c8b3f6f13fafb3026b24ba43c6a695a0c76179b844369"
|
||||
dependencies = [
|
||||
"log",
|
||||
"termcolor",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -411,6 +445,15 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.44"
|
||||
|
@ -431,6 +474,36 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.11"
|
||||
|
@ -458,6 +531,37 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,17 +1,24 @@
|
|||
[package]
|
||||
name = "disco-rpc"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "A customisable client for Discord rich presence using simple Lua configuration."
|
||||
authors = ["Kaitlyn~Ethylia <kaitlyyn.ethylia@proton.me>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KaitlynEthylia/Disco"
|
||||
license = "Unlicense"
|
||||
exclude = ["/etc/*"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.3.19", features = ["derive"] }
|
||||
clap = { version = "4.3.21", features = ["derive", "env", "string"] }
|
||||
dirs = "5.0.1"
|
||||
discord-rich-presence = "0.2.3"
|
||||
log = "0.4.20"
|
||||
mlua = { version = "0.8.9", features = ["lua54", "send"] }
|
||||
simplelog = "0.12.1"
|
||||
|
||||
[features]
|
||||
unsafe = []
|
||||
|
||||
[[bin]]
|
||||
name = "disco"
|
||||
path = "src/main.rs"
|
||||
|
|
37
README.md
37
README.md
|
@ -11,7 +11,7 @@
|
|||
[![Crates.io](https://img.shields.io/crates/v/disco-rpc?color=%23f7b679&logo=rust&style=for-the-badge)](https://crates.io/crates/disco-rpc)
|
||||
[![Unlicense](https://img.shields.io/crates/l/terny?color=bfdfff&logo=unlicense&style=for-the-badge)](https://unlicense.org/)
|
||||
|
||||
Disco is a customisable client for Discord rich presence using
|
||||
Disco is a customisable client for Discord rich presence using
|
||||
simple Lua configuration.
|
||||
|
||||
</div>
|
||||
|
@ -69,13 +69,16 @@ programme from running and print the location that it would otherwise
|
|||
look for a configuration file.
|
||||
The other flags that actually affect the programme are:
|
||||
|
||||
**-c, --config <CONFIG>**: Overrides the default path to look for configuration.
|
||||
**-c, --config <FILE>**: Override the default configuration path.
|
||||
<br />
|
||||
**-i, --client-id <CLIENT_ID>**: Sets the ID of the application to connect as. Takes precedent over Lua configuration.
|
||||
**-i, --application-id <ID>**: Set the ID of the Discord application to connect to.
|
||||
<br />
|
||||
**-r, --retry-after <DELAY>**: If connecting to Discord fails, retry after DELAY seconds [default: 0]
|
||||
**-r, --retry-after <DELAY>**: Retry after a failed connection.
|
||||
<br />
|
||||
**-q, --quiet ...**: Disables printing excess information.
|
||||
<br />
|
||||
**-d, --dry-run**: Parse the config but don't connect to Discord.
|
||||
<br />
|
||||
**-q, --quiet**: Don't print any text to the console.
|
||||
|
||||
<a id="configuration" />
|
||||
|
||||
|
@ -151,6 +154,11 @@ returning. Once it stops, the final value remains.
|
|||
This example is contrived, but a more complex example can be seen
|
||||
in [the example section](#example)
|
||||
|
||||
Additionally. As well as assigning a value directly, a function can be
|
||||
given which will immediately return the value. This ensured that the
|
||||
value is evaluated only once, despite the application having to launch
|
||||
multiple Lua VMs.
|
||||
|
||||
Disco may also call external lua libraries, however, if that
|
||||
library requires C libraries, the `unsafe` feature flag will need to
|
||||
be enabled.
|
||||
|
@ -174,9 +182,9 @@ but I have modified it slightly to demonstrate some features that
|
|||
I didn't use, such as polling.
|
||||
|
||||
```lua
|
||||
-- ID of the client I created for ArchLinux via
|
||||
-- ID of the application I created for ArchLinux via
|
||||
-- https://discord.com/developers/applications
|
||||
ClientID = 1137762526541656105
|
||||
ApplicationID = 1137762526541656105
|
||||
|
||||
-- Display the rich presence
|
||||
Active = true
|
||||
|
@ -219,17 +227,6 @@ Button1 = {
|
|||
}
|
||||
```
|
||||
|
||||
## Before Release
|
||||
### The Result
|
||||
|
||||
Things that intend to be done before i could be happy calling this a
|
||||
1.0.0 project.
|
||||
|
||||
- [ ] Provide extra utilitly functions to make some tasks easier.
|
||||
- [ ] Test compaility on non x86_64-linux-gnu machines, and fix where possible.
|
||||
- [ ] Provide bash, zsh, and fish completions with the releases.
|
||||
- [ ] Provide template systemd and potentially runit and openrc services for running on startup.
|
||||
- [ ] Fix bugs that are more than likely to show up, and provide friendlier error handling.
|
||||
|
||||
None of these goals are particularly quick and easy, so they are
|
||||
likely to only end up completed if enough interest is shown in the
|
||||
tool.
|
||||
![Discord Rich Presence](/etc/images/example.png)
|
||||
|
|
BIN
etc/assets/example.png
Normal file
BIN
etc/assets/example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
9
etc/completions/fish/disco.fish
Normal file
9
etc/completions/fish/disco.fish
Normal file
|
@ -0,0 +1,9 @@
|
|||
complete -c disco -f
|
||||
complete -c disco -s 'V' -l 'version' -d "Print version"
|
||||
complete -c disco -s 'h' -l 'help' -d "Print help"
|
||||
complete -c disco -s 'c' -l 'config' -d "Override the default configuration path." -F
|
||||
complete -c disco -s 'i' -l 'application-id' -d "Set the ID of the Discord application to connect to."
|
||||
complete -c disco -s 'r' -l 'retry-after' -d "Retry after a failed connection."
|
||||
complete -c disco -s 'q' -l 'quiet' -d "Disables printing excess information." -x -a "0 1 2"
|
||||
complete -c disco -s 'p' -l 'print-config-path' -d "Print the default configuration location."
|
||||
complete -c disco -s 'd' -l 'dry-run' -d "Parse the config but don't connect to Discord."
|
19
etc/completions/zsh/_disco
Normal file
19
etc/completions/zsh/_disco
Normal file
|
@ -0,0 +1,19 @@
|
|||
#compdef disco
|
||||
|
||||
# Save this file as _exa in /usr/local/share/zsh/site-functions or in any
|
||||
# other folder in $fpath. E.g. save it in a folder called ~/.zfunc and add a
|
||||
# line containing `fpath=(~/.zfunc $fpath)` somewhere before `compinit` in your
|
||||
# ~/.zshrc.
|
||||
|
||||
__disco() {
|
||||
_arguments -s -S \
|
||||
"(- *)"{-V,--version}"[Print version]" \
|
||||
"(- *)"{-h,--help}"[Print help]" \
|
||||
{-c,--config}"[Override the default configuration path.]:(file)" \
|
||||
{-i,--application-id}"[Set the ID of the Discord application to connect to.]:(id)" \
|
||||
{-r,--retry-after}"[Retry after a failed connection.]:(seconds)" \
|
||||
{-q,--quiet}"[Disables printing excess information.]:(quiet):(0 1 2)" \
|
||||
{-p,--print-config-path}"[Print the default configuration location.]" \
|
||||
{-d,--dry-run}"[Parse the config but don't connect to Discord.]"
|
||||
}
|
||||
__disco
|
23
etc/services/openrc/disco
Normal file
23
etc/services/openrc/disco
Normal file
|
@ -0,0 +1,23 @@
|
|||
#!/sbin/openrc-run
|
||||
#
|
||||
# NOTE: This is a service for the openrc init system.
|
||||
#
|
||||
# How to use this service:
|
||||
#
|
||||
# 1. Place this file in the `/etc/init.d/' directory
|
||||
#
|
||||
# 2. Start the service
|
||||
#
|
||||
# rc-service disco start
|
||||
#
|
||||
# 3. If everything works as intended, add service to default runlevel as it will
|
||||
# run after boot runlevel
|
||||
#
|
||||
# rc-update add disco default
|
||||
#
|
||||
# The paths below should be absolute paths.
|
||||
|
||||
command=/path/to/disco
|
||||
command_args="--retry-after <int>"
|
||||
command_background="true"
|
||||
|
31
etc/services/runit/run
Normal file
31
etc/services/runit/run
Normal file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# NOTE: This is a service for the runit init system! If you are using
|
||||
# something else (openrc, sysvinit, systemd, ...) then you do not
|
||||
# need to use this file.
|
||||
#
|
||||
# How to use this service:
|
||||
#
|
||||
# 1. Place this file in the `/etc/sv/disco' directory. You will need
|
||||
# to create this directory; this is most easily done with `xmksv'
|
||||
# from the `xtools' package:
|
||||
#
|
||||
# xmksv disco
|
||||
#
|
||||
# 2. Symlink to `/var/service':
|
||||
#
|
||||
# ln -s /etc/sv/disco /var/service
|
||||
#
|
||||
# Note 2: xmksv can create by default a `down` file that disable
|
||||
# disco to startup with system, if that happens just delete this file;
|
||||
# see [2] for further details.
|
||||
#
|
||||
# The paths below should be absolute paths. You can put environment
|
||||
# variables in a `conf' file that's in the same directory as this file
|
||||
# and refer to them here if you want; see [1] for further details.
|
||||
#
|
||||
# [1] https://docs.voidlinux.org/config/services/index.html#service-directories
|
||||
# [2] https://docs.voidlinux.org/config/services/index.html#enabling-services
|
||||
|
||||
exec 1>&2
|
||||
exec /path/to/disco --retry-after <int> || exit 1
|
5
etc/services/systemd/disco.service
Normal file
5
etc/services/systemd/disco.service
Normal file
|
@ -0,0 +1,5 @@
|
|||
[Unit]
|
||||
Description=Disco - Discord Rich Presence client
|
||||
|
||||
[Service]
|
||||
ExecStart=/path/to/disco --retry-after <int>
|
152
src/command.rs
Normal file
152
src/command.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use std::{fs, path::PathBuf};
|
||||
|
||||
use log::{error, info, warn};
|
||||
|
||||
use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode};
|
||||
|
||||
use clap::{value_parser, Parser};
|
||||
|
||||
use crate::Disco;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
//TODO disco2 vs disco vs disco-rpc
|
||||
// Waiting on https://github.com/clap-rs/clap/issues/3221
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Command {
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "DISCO_CONFIG",
|
||||
value_name = "FILE",
|
||||
help = "Override the default configuration path.",
|
||||
default_value = get_path().into_os_string()
|
||||
)]
|
||||
config: PathBuf,
|
||||
|
||||
#[arg(
|
||||
short = 'i',
|
||||
long,
|
||||
env = "DISCO_APPLICATION_ID",
|
||||
value_name = "ID",
|
||||
help = "Set the ID of the Discord application to connect to.",
|
||||
long_help = "Set the ID of the Discord application to connect to. \
|
||||
This value takes precedent over the value set in Lua.",
|
||||
value_parser = value_parser!(u64).range(10000000000000000..=999999999999999999)
|
||||
)]
|
||||
application_id: Option<u64>,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "DISCO_RETRY_AFTER",
|
||||
value_name = "DELAY",
|
||||
default_value_t = 0,
|
||||
help = "Retry after a failed connection.",
|
||||
long_help = "If connecting to Discord fails, keep retrying every DELAY \
|
||||
seconds."
|
||||
)]
|
||||
retry_after: usize,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "DISCO_QUIET",
|
||||
action = clap::ArgAction::Count,
|
||||
help = "Disables printing excess information.",
|
||||
long_help = "Disable printing information about the running process.
|
||||
Set twice to disable output completely, including errors.",
|
||||
value_parser = value_parser!(u8).range(0..=2)
|
||||
)]
|
||||
quiet: u8,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "Print the default configuration location.",
|
||||
long_help = "Halts normal execution and prints the location that Disco \
|
||||
will attempt to use for configuration by default."
|
||||
)]
|
||||
print_config_path: bool,
|
||||
|
||||
#[arg(short, long, help = "Parse the config but don't connect to Discord.")]
|
||||
dry_run: bool,
|
||||
|
||||
#[cfg(feature = "unsafe")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "Run the Lua VM in safe mode.",
|
||||
long_help = "Run the Lua VM in safe mode, meaning it will not be able \
|
||||
to load any C libraries. This option is only available when compiled \
|
||||
with the unsafe feature flag."
|
||||
)]
|
||||
safe: bool,
|
||||
}
|
||||
|
||||
fn get_path() -> PathBuf {
|
||||
dirs::config_local_dir()
|
||||
.unwrap_or(PathBuf::new())
|
||||
.join("disco.lua")
|
||||
}
|
||||
|
||||
fn validate_path(pathbuf: &PathBuf) -> bool {
|
||||
let path = pathbuf.as_path();
|
||||
path.exists() && path.is_file()
|
||||
}
|
||||
|
||||
macro_rules! unwrap_error {
|
||||
($expr:expr, $msg:literal) => {
|
||||
match $expr {
|
||||
Ok(val) => val,
|
||||
Err(_) => {
|
||||
error!($msg);
|
||||
return None;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init() -> Option<Disco> {
|
||||
let args = Command::parse();
|
||||
|
||||
let log_level = match args.quiet {
|
||||
0 => LevelFilter::Debug,
|
||||
1 => LevelFilter::Warn,
|
||||
2.. => LevelFilter::Off,
|
||||
};
|
||||
|
||||
TermLogger::init(
|
||||
log_level,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
)
|
||||
.expect("Failed to setup logging");
|
||||
|
||||
let valid_path = validate_path(&args.config);
|
||||
|
||||
if args.print_config_path {
|
||||
info!("Config path is {}", args.config.display());
|
||||
if !valid_path {
|
||||
warn!("Config file does not exist");
|
||||
};
|
||||
return None;
|
||||
}
|
||||
|
||||
if !valid_path {
|
||||
error!("Config file does not exist");
|
||||
return None;
|
||||
};
|
||||
|
||||
let data = unwrap_error!(fs::read(&args.config), "Failed to open config file.");
|
||||
let data = unwrap_error!(String::from_utf8(data), "Config is not valid UTF-8.");
|
||||
|
||||
return Some(Disco {
|
||||
retry_after: args.retry_after,
|
||||
dry_run: args.dry_run,
|
||||
application_id: args.application_id,
|
||||
config_data: data,
|
||||
#[cfg(feature = "unsafe")]
|
||||
safe: args.safe,
|
||||
});
|
||||
}
|
11
src/library.rs
Normal file
11
src/library.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
pub static DISCO_LIB: &str = "
|
||||
function watch(command, err)
|
||||
return coroutine.create(function()
|
||||
local handle = io.popen(_)
|
||||
if not handle then return err end
|
||||
for line in handle:lines() do
|
||||
coroutine.yield(line)
|
||||
end
|
||||
end)
|
||||
end
|
||||
";
|
53
src/lua.rs
53
src/lua.rs
|
@ -29,6 +29,11 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Variable<T> {
|
|||
}
|
||||
},
|
||||
"thread" => Self::Listen,
|
||||
"function" => {
|
||||
let fun = LuaFunction::from_lua(lua_value, lua)?;
|
||||
let val = fun.call(())?;
|
||||
Self::from_lua(val, lua)?
|
||||
},
|
||||
_ => Self::Static(T::from_lua(lua_value, lua)?),
|
||||
})
|
||||
}
|
||||
|
@ -40,6 +45,7 @@ impl<T: Clone + for<'lua> FromLua<'lua> + Send + 'static> Variable<T> {
|
|||
name: &'static str,
|
||||
data: String,
|
||||
dofun: F,
|
||||
#[cfg(feature = "unsafe")] safe: bool,
|
||||
) -> Option<JoinHandle<()>> {
|
||||
match self {
|
||||
Self::Static(val) => {
|
||||
|
@ -47,7 +53,10 @@ impl<T: Clone + for<'lua> FromLua<'lua> + Send + 'static> Variable<T> {
|
|||
None
|
||||
},
|
||||
Self::Poll(rate) => Some(thread::spawn(move || {
|
||||
let lua = get_lua();
|
||||
let lua = get_lua(
|
||||
#[cfg(feature = "unsafe")]
|
||||
safe,
|
||||
);
|
||||
lua.load(&data).exec().unwrap();
|
||||
let fun: LuaFunction = lua
|
||||
.globals()
|
||||
|
@ -62,7 +71,10 @@ impl<T: Clone + for<'lua> FromLua<'lua> + Send + 'static> Variable<T> {
|
|||
}
|
||||
})),
|
||||
Self::Listen => Some(thread::spawn(move || {
|
||||
let lua = get_lua();
|
||||
let lua = get_lua(
|
||||
#[cfg(feature = "unsafe")]
|
||||
safe,
|
||||
);
|
||||
lua.load(&data).exec().unwrap();
|
||||
let thread: LuaThread = lua.globals().get(name).unwrap();
|
||||
loop {
|
||||
|
@ -76,6 +88,7 @@ impl<T: Clone + for<'lua> FromLua<'lua> + Send + 'static> Variable<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unsafe"))]
|
||||
macro_rules! watchtype {
|
||||
($var:ident, $ty:ty, $name:ident, $send:ident, $ctx:ident, $env:ident) => {
|
||||
$env.get::<_, Variable<$ty>>($name)?
|
||||
|
@ -85,6 +98,7 @@ macro_rules! watchtype {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unsafe"))]
|
||||
pub fn create_watcher(
|
||||
name: &'static str,
|
||||
send: Sender<ActivityData>,
|
||||
|
@ -103,3 +117,38 @@ pub fn create_watcher(
|
|||
ActivityDataTag::SmallImage => watchtype!(SmallImage, Image, name, send, ctx, env),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unsafe")]
|
||||
macro_rules! watchtype {
|
||||
($var:ident, $ty:ty, $name:ident, $send:ident, $ctx:ident, $env:ident, $safe:ident) => {
|
||||
$env.get::<_, Variable<$ty>>($name)?.watch(
|
||||
$name,
|
||||
$ctx.clone(),
|
||||
move |val| $send.send(ActivityData::$var(val)).unwrap(),
|
||||
$safe,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "unsafe")]
|
||||
pub fn create_watcher(
|
||||
name: &'static str,
|
||||
send: Sender<ActivityData>,
|
||||
ctx: &String,
|
||||
env: &LuaTable,
|
||||
tag: &ActivityDataTag,
|
||||
safe: bool,
|
||||
) -> Result<Option<JoinHandle<()>>, LuaError> {
|
||||
Ok(match tag {
|
||||
ActivityDataTag::Active => watchtype!(Active, bool, name, send, ctx, env, safe),
|
||||
ActivityDataTag::State => watchtype!(State, String, name, send, ctx, env, safe),
|
||||
ActivityDataTag::Details => watchtype!(Details, String, name, send, ctx, env, safe),
|
||||
ActivityDataTag::Timestamp => watchtype!(Timestamp, Timestamp, name, send, ctx, env, safe),
|
||||
ActivityDataTag::FirstButton => watchtype!(FirstButton, Button, name, send, ctx, env, safe),
|
||||
ActivityDataTag::SecondButton => {
|
||||
watchtype!(SecondButton, Button, name, send, ctx, env, safe)
|
||||
},
|
||||
ActivityDataTag::LargeImage => watchtype!(LargeImage, Image, name, send, ctx, env, safe),
|
||||
ActivityDataTag::SmallImage => watchtype!(SmallImage, Image, name, send, ctx, env, safe),
|
||||
})
|
||||
}
|
||||
|
|
179
src/main.rs
179
src/main.rs
|
@ -1,13 +1,10 @@
|
|||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
use std::{env, path::PathBuf};
|
||||
use std::{fs, thread};
|
||||
use std::{process, thread};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use dirs::config_dir;
|
||||
use discord_rich_presence::activity::{Activity, Assets, Button, Timestamps};
|
||||
use discord_rich_presence::{DiscordIpc, DiscordIpcClient};
|
||||
use log::{error, info, warn};
|
||||
use mlua::Lua;
|
||||
|
||||
use crate::activity::ActivityData;
|
||||
|
@ -16,97 +13,88 @@ use crate::activity::{Button as DiscoButton, Image, Timestamp};
|
|||
use crate::lua::create_watcher;
|
||||
|
||||
mod activity;
|
||||
mod command;
|
||||
mod library;
|
||||
mod lua;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Command {
|
||||
/// Overrides the default path to look for configuration.
|
||||
#[arg(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// Sets the ID of the application to connect as. Takes prescedent over Lua configuration.
|
||||
#[arg(short = 'i', long)]
|
||||
client_id: Option<String>,
|
||||
|
||||
/// If connecting to Discord fails, retry after DELAY seconds.
|
||||
#[arg(short, long, value_name = "DELAY", default_value_t = 0)]
|
||||
retry_after: u64,
|
||||
|
||||
/// Don't print any text to the console.
|
||||
#[arg(short, long)]
|
||||
quiet: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
print_config_path: bool,
|
||||
#[derive(Debug)]
|
||||
pub struct Disco {
|
||||
pub retry_after: usize,
|
||||
pub dry_run: bool,
|
||||
pub application_id: Option<u64>,
|
||||
pub config_data: String,
|
||||
#[cfg(feature = "unsafe")]
|
||||
pub safe: bool,
|
||||
}
|
||||
|
||||
pub fn get_lua() -> Lua {
|
||||
pub fn get_lua(#[cfg(feature = "unsafe")] safe: bool) -> Lua {
|
||||
#[cfg(not(feature = "unsafe"))]
|
||||
return Lua::new();
|
||||
let lua = Lua::new();
|
||||
#[cfg(feature = "unsafe")]
|
||||
unsafe {
|
||||
return Lua::unsafe_new();
|
||||
let lua = if !safe {
|
||||
unsafe { Lua::unsafe_new() }
|
||||
} else {
|
||||
Lua::new()
|
||||
};
|
||||
if let Err(_) = lua.load(library::DISCO_LIB).exec() {
|
||||
warn!("Failed to load provided builtin functions, only the Lua standard library will be available");
|
||||
};
|
||||
lua
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Command::parse();
|
||||
let path = match args.config {
|
||||
Some(path) => path,
|
||||
None => env::var("DISCO_CONFIG")
|
||||
.map(|val| PathBuf::from(val))
|
||||
.unwrap_or(
|
||||
config_dir()
|
||||
.expect(
|
||||
"Could not find a place to look for config. Please
|
||||
specify a file in the command line or set
|
||||
DISCO_CONFIG variable.",
|
||||
)
|
||||
.join("disco.lua"),
|
||||
),
|
||||
let args = match command::init() {
|
||||
Some(disco) => disco,
|
||||
None => process::exit(0),
|
||||
};
|
||||
|
||||
if args.print_config_path {
|
||||
println!("{:?}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
let data = fs::read(&path).expect(&format!("Failed to read config file at {path:?}"));
|
||||
let file = String::from_utf8(data).expect("Contents of config file is not valid UTF-8");
|
||||
|
||||
let lua = get_lua();
|
||||
lua.load(&file).exec().unwrap();
|
||||
let lua = get_lua(
|
||||
#[cfg(feature = "unsafe")]
|
||||
args.safe,
|
||||
);
|
||||
lua.load(&args.config_data).exec().unwrap();
|
||||
let env = lua.globals();
|
||||
|
||||
let client_id = match args.client_id {
|
||||
Some(id) => id,
|
||||
None => env
|
||||
.get("ClientID")
|
||||
.expect("No client id available. Set ClientID in {path} or with --client-id"),
|
||||
let application_id = match args.application_id {
|
||||
Some(id) => id.to_string(),
|
||||
None => match env.get("ApplicationID") {
|
||||
Ok(application_id) => application_id,
|
||||
Err(_) => {
|
||||
error!("No application id available. Set ApplicationID in disco.lua or with --application-id");
|
||||
process::exit(0);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if !args.quiet {
|
||||
println!("Client ID: {client_id}");
|
||||
}
|
||||
info!("Application ID: {application_id}");
|
||||
|
||||
let mut client =
|
||||
DiscordIpcClient::new(&client_id).expect("Failed to create Discord IPC Client");
|
||||
let mut ret = client.connect();
|
||||
match args.retry_after {
|
||||
0 => {
|
||||
if let Err(_) = ret {
|
||||
panic!("Failed to connect to Discord IPC. Please make sure Discord is open.");
|
||||
}
|
||||
},
|
||||
n => {
|
||||
while let Err(_) = ret {
|
||||
println!("Failed to connect to Discord IPC. Retrying in {n} seconds...");
|
||||
thread::sleep(Duration::from_secs(n));
|
||||
ret = client.connect();
|
||||
}
|
||||
let mut client = match args.dry_run {
|
||||
false => Some(
|
||||
DiscordIpcClient::new(&application_id).expect("Failed to create Discord IPC Client"),
|
||||
),
|
||||
true => {
|
||||
info!("Performing dry run. Won't attempt to connect to Discord");
|
||||
None
|
||||
},
|
||||
};
|
||||
if let Some(ref mut client) = client {
|
||||
let mut ret = client.connect();
|
||||
match args.retry_after {
|
||||
0 => {
|
||||
if let Err(_) = ret {
|
||||
error!("Failed to connect to Discord IPC. Please make sure Discord is open.");
|
||||
process::exit(0);
|
||||
}
|
||||
},
|
||||
n => {
|
||||
while let Err(_) = ret {
|
||||
warn!("Failed to connect to Discord IPC. Retrying in {n} seconds...");
|
||||
thread::sleep(Duration::from_secs(n as u64));
|
||||
ret = client.connect();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
let (send, recv) = mpsc::channel::<ActivityData>();
|
||||
|
||||
|
@ -122,11 +110,17 @@ fn main() {
|
|||
];
|
||||
|
||||
values.iter().for_each(|(name, ty)| {
|
||||
let ret = create_watcher(name, send.clone(), &file, &env, &ty);
|
||||
let ret = create_watcher(
|
||||
name,
|
||||
send.clone(),
|
||||
&args.config_data,
|
||||
&env,
|
||||
&ty,
|
||||
#[cfg(feature = "unsafe")]
|
||||
args.safe,
|
||||
);
|
||||
if let Err(_) = ret {
|
||||
if !args.quiet {
|
||||
println!("No value for {name}");
|
||||
}
|
||||
warn!("No value for {name}");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -135,13 +129,13 @@ fn main() {
|
|||
loop {
|
||||
match recv.recv() {
|
||||
Ok(val) => {
|
||||
if !args.quiet {
|
||||
println!("New Value: {val:?}");
|
||||
}
|
||||
info!("New Value: {val:?}");
|
||||
match val {
|
||||
ActivityData::Active(val) => {
|
||||
if !val {
|
||||
let _ = client.clear_activity();
|
||||
if let Some(ref mut client) = client {
|
||||
if !val {
|
||||
let _ = client.clear_activity();
|
||||
}
|
||||
}
|
||||
activity.active = val
|
||||
},
|
||||
|
@ -153,12 +147,12 @@ fn main() {
|
|||
ActivityData::LargeImage(val) => activity.large_image = Some(val),
|
||||
ActivityData::SmallImage(val) => activity.small_image = Some(val),
|
||||
}
|
||||
activity.process(&mut client, args.quiet);
|
||||
if let Some(ref mut client) = client {
|
||||
activity.process(client);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
if !args.quiet {
|
||||
println!("Exiting...");
|
||||
}
|
||||
info!("Exiting...");
|
||||
break;
|
||||
},
|
||||
}
|
||||
|
@ -191,7 +185,7 @@ impl DiscoActivity {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, client: &mut DiscordIpcClient, quiet: bool) {
|
||||
fn process(&self, client: &mut DiscordIpcClient) {
|
||||
if self.active {
|
||||
let mut activity = Activity::new();
|
||||
|
||||
|
@ -247,9 +241,6 @@ impl DiscoActivity {
|
|||
activity = activity.assets(assets);
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
println!("DEBUG: {self:#?}");
|
||||
}
|
||||
client.set_activity(activity).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue