diff --git a/Cargo.lock b/Cargo.lock index 79f65d9f..5cf54602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,20 +106,6 @@ dependencies = [ "twilight-http", ] -[[package]] -name = "app-commands" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures", - "libpk", - "tokio", - "tracing", - "twilight-http", - "twilight-model", - "twilight-util", -] - [[package]] name = "arc-swap" version = "1.7.1" @@ -4588,7 +4574,6 @@ version = "0.16.0" source = "git+https://github.com/pluralkit/twilight?branch=pluralkit-7f08d95#054a2aa5d29fb46220af1cd5df568b73511cdb26" dependencies = [ "twilight-model", - "twilight-validate", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3707ce77..f32165b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ axum = { git = "https://github.com/pluralkit/axum", branch = "v0.8.4-pluralkit" twilight-gateway = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95" } twilight-cache-inmemory = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95", features = ["permission-calculator"] } -twilight-util = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95", features = ["permission-calculator", "builder"] } +twilight-util = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95", features = ["permission-calculator"] } twilight-model = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95" } twilight-http = { git = "https://github.com/pluralkit/twilight", branch = "pluralkit-7f08d95", default-features = false, features = ["rustls-aws_lc_rs", "rustls-native-roots"] } diff --git a/PluralKit.Bot/Proxy/ProxyService.cs b/PluralKit.Bot/Proxy/ProxyService.cs index ee6108af..8a59957d 100644 --- a/PluralKit.Bot/Proxy/ProxyService.cs +++ b/PluralKit.Bot/Proxy/ProxyService.cs @@ -66,15 +66,6 @@ public class ProxyService var autoproxySettings = await _repo.GetAutoproxySettings(ctx.SystemId.Value, guild.Id, null); - if (IsDisableAutoproxy(message)) - { - await _repo.UpdateAutoproxy(ctx.SystemId.Value, guild.Id, null, new() - { - AutoproxyMode = AutoproxyMode.Off - }); - return false; - } - if (autoproxySettings.AutoproxyMode == AutoproxyMode.Latch && IsUnlatch(message)) { // "unlatch" @@ -504,9 +495,6 @@ public class ProxyService public static bool IsUnlatch(Message message) => message.Content.StartsWith(@"\\") || message.Content.StartsWith("\\\u200b\\"); - public static bool IsDisableAutoproxy(Message message) - => message.Content.StartsWith(@"\\\") || message.Content.StartsWith("\\\u200b\\\u200b\\"); - private async Task HandleProxyExecutedActions(MessageContext ctx, AutoproxySettings autoproxySettings, Message triggerMessage, Message proxyMessage, ProxyMatch match, bool deletePrevious = true) diff --git a/crates/app-commands/Cargo.toml b/crates/app-commands/Cargo.toml deleted file mode 100644 index 11ab75d4..00000000 --- a/crates/app-commands/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "app-commands" -version = "0.1.0" -edition = "2024" - -[dependencies] -libpk = { path = "../libpk" } -anyhow = { workspace = true } -futures = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } -twilight-http = { workspace = true } -twilight-model = { workspace = true } -twilight-util = { workspace = true } diff --git a/crates/app-commands/src/main.rs b/crates/app-commands/src/main.rs deleted file mode 100644 index a03f5b70..00000000 --- a/crates/app-commands/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -use twilight_model::{ - application::command::{Command, CommandType}, - guild::IntegrationApplication, -}; -use twilight_util::builder::command::CommandBuilder; - -#[libpk::main] -async fn main() -> anyhow::Result<()> { - let discord = twilight_http::Client::builder() - .token( - libpk::config - .discord - .as_ref() - .expect("missing discord config") - .bot_token - .clone(), - ) - .build(); - - let interaction = discord.interaction(twilight_model::id::Id::new( - libpk::config - .discord - .as_ref() - .expect("missing discord config") - .client_id - .clone() - .get(), - )); - - let commands = vec![ - // message commands - // description must be empty string - CommandBuilder::new("\u{2753} Message info", "", CommandType::Message).build(), - CommandBuilder::new("\u{274c} Delete message", "", CommandType::Message).build(), - CommandBuilder::new("\u{1f514} Ping author", "", CommandType::Message).build(), - ]; - - interaction.set_global_commands(&commands).await?; - - Ok(()) -} diff --git a/scripts/app-commands/.gitignore b/scripts/app-commands/.gitignore new file mode 100644 index 00000000..3e370e3f --- /dev/null +++ b/scripts/app-commands/.gitignore @@ -0,0 +1,4 @@ +/commands.json + +*.pyc +__pycache__/ diff --git a/scripts/app-commands/README.md b/scripts/app-commands/README.md new file mode 100644 index 00000000..9d8d576a --- /dev/null +++ b/scripts/app-commands/README.md @@ -0,0 +1,23 @@ +# PluralKit "application command" helpers + +## Adding new commands + +Edit the `COMMAND_LIST` global in `commands.py`, making sure that any +command names that are specified in that file match up with the +command names used in the bot code (which will generally be in the list +in `PluralKit.Bot/ApplicationCommandMeta/ApplicationCommandList.cs`). + +TODO: add helpers for slash commands to this + +## Dumping application command JSON + +Run `python3 commands.py` to get a JSON dump of the available application +commands - this is in a format that can be sent to Discord as a `PUT` to +`/applications/{clientId}/commands`. + +## Updating Discord's list of application commands + +From the root of the repository (where your `pluralkit.conf` resides), +run `python3 ./scripts/app-commands/update.py`. This will **REPLACE** +any existing application commands that Discord knows about, with the +updated list. \ No newline at end of file diff --git a/scripts/app-commands/commands.py b/scripts/app-commands/commands.py new file mode 100644 index 00000000..6a908ba7 --- /dev/null +++ b/scripts/app-commands/commands.py @@ -0,0 +1,10 @@ +from common import * + +COMMAND_LIST = [ + MessageCommand("\U00002753 Message info"), + MessageCommand("\U0000274c Delete message"), + MessageCommand("\U0001f514 Ping author"), +] + +if __name__ == "__main__": + print(__import__('json').dumps(COMMAND_LIST)) \ No newline at end of file diff --git a/scripts/app-commands/common/__init__.py b/scripts/app-commands/common/__init__.py new file mode 100644 index 00000000..2a53ba18 --- /dev/null +++ b/scripts/app-commands/common/__init__.py @@ -0,0 +1 @@ +from .types import MessageCommand \ No newline at end of file diff --git a/scripts/app-commands/common/types.py b/scripts/app-commands/common/types.py new file mode 100644 index 00000000..65c35232 --- /dev/null +++ b/scripts/app-commands/common/types.py @@ -0,0 +1,7 @@ +class MessageCommand(dict): + COMMAND_TYPE = 3 + + def __init__(self, name): + super().__init__() + self["type"] = self.__class__.COMMAND_TYPE + self["name"] = name \ No newline at end of file diff --git a/scripts/app-commands/update.py b/scripts/app-commands/update.py new file mode 100644 index 00000000..3005db0a --- /dev/null +++ b/scripts/app-commands/update.py @@ -0,0 +1,70 @@ +from common import * +from commands import COMMAND_LIST + +import io +import os +import sys +import json + +from pathlib import Path +from urllib import request +from urllib.error import URLError + +DISCORD_API_BASE = "https://discord.com/api/v10" + +def get_config(): + data = {} + + # prefer token from environment if present + envbase = ["PluralKit", "Bot"] + for var in ["Token", "ClientId"]: + for sep in [':', '__']: + envvar = sep.join(envbase + [var]) + if envvar in os.environ: + data[var] = os.environ[envvar] + + if "Token" in data and "ClientId" in data: + return data + + # else fall back to config + cfg_path = Path(os.getcwd()) / "pluralkit.conf" + if cfg_path.exists(): + cfg = {} + with open(str(cfg_path), 'r') as fh: + cfg = json.load(fh) + + if 'PluralKit' in cfg and 'Bot' in cfg['PluralKit']: + return cfg['PluralKit']['Bot'] + + return None + +def main(): + config = get_config() + if config is None: + raise ArgumentError("config was not loaded") + if 'Token' not in config or 'ClientId' not in config: + raise ArgumentError("config is missing 'Token' or 'ClientId'") + + data = json.dumps(COMMAND_LIST) + url = DISCORD_API_BASE + f"/applications/{config['ClientId']}/commands" + req = request.Request(url, method='PUT', data=data.encode('utf-8')) + req.add_header("Content-Type", "application/json") + req.add_header("Authorization", f"Bot {config['Token']}") + req.add_header("User-Agent", "PluralKit (app-commands updater; https://pluralkit.me)") + + try: + with request.urlopen(req) as resp: + if resp.status == 200: + print("Update successful!") + return 0 + + except URLError as resp: + print(f"[!!!] Update not successful: status {resp.status}", file=sys.stderr) + print(f"[!!!] Response body below:\n", file=sys.stderr) + print(resp.read(), file=sys.stderr) + sys.stderr.flush() + + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/scripts/dump-db.sh b/scripts/dump-db.sh new file mode 100755 index 00000000..d794dab4 --- /dev/null +++ b/scripts/dump-db.sh @@ -0,0 +1,3 @@ +#!/bin/sh +docker-compose -f "$(dirname $0)/../docker-compose.yml" exec -T -u postgres db pg_dump postgres + diff --git a/scripts/rclone-db.sh b/scripts/rclone-db.sh new file mode 100755 index 00000000..6e4fda7c --- /dev/null +++ b/scripts/rclone-db.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Usage: rclone-db.sh : +# eg. rclone-db.sh b2:pluralkit + +FILENAME=pluralkit-$(date -u +"%Y-%m-%dT%H:%M:%S").sql.gz + +echo Dumping database to /tmp/$FILENAME... +$(dirname $0)/dump-db.sh | gzip > /tmp/$FILENAME + +echo Transferring to remote $1... +rclone -P copy /tmp/$FILENAME $1 + +echo Cleaning up... +rm /tmp/$FILENAME \ No newline at end of file diff --git a/scripts/run-test-db.sh b/scripts/run-test-db.sh new file mode 100755 index 00000000..272d24e5 --- /dev/null +++ b/scripts/run-test-db.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# Runs a local database in the background listening on port 5432, deleting itself once stopped +# Requires Docker. May need sudo if your user isn't in the `docker` group. +docker run --rm --detach --publish 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:alpine \ No newline at end of file