Many fixes and tweaks
* Fix tags (again?) * Make tag names more consistent * Remove prefix commands (not implemented well and not worth fixing) * Allow '-'s in tags * Fix /joke * Fix /members * Fix intel_hd issue match * Fix log analysis reply * Clearer log analysis messages It's weird to say the process failed when no issues were found. * Clippy * Final doc cleanup * Fix link expanding * Fix duplicate event filtering The other code simply does not work. ChannelId does have a method to grab members but I'm not sure whether it would work either. * Remove message resolution It's surprisingly hard to create an bug-free implementation. * Fix pluralkit detection * simplify tag codegen * commands: improve error handling in members unwrap() bad!!!11!! * events: use debug logs for pk checks * Revert "Remove message resolution" This reverts commit 0d9f224a81917212adafdeb2213f3cc11b44cf88. * Bring back prefix commands with "." (it's easier to type) * Add help * Fix messsage resolution * utils: factor out message resolution * Improve tag message * Disable VC support for message resolution for now * Improve prefix command usage Update on edit, display additional tip with wrong usage. * Check invoke_on_edit to display tip * Add defer in commands which make http requests * Apply tag sorting to slash commands too * handlers::error: `+=` -> `writeln!` * handlers::event: ignore own new messages * help: remove unneeded format! * optimize for size in release builds * nix: cleanup deployment expressions * nix: use treefmt * nix: update flake.lock Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/eb683549b7d76b12d1a009f888b91b70ed34485f' (2024-01-27) → 'github:nix-community/fenix/c53bb4a32f2fce7acf4e8e160a54779c4460ffdb' (2024-03-17) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/596e5c77cf5b2b660b3ac2ce732fa0596c246d9b' (2024-01-26) → 'github:rust-lang/rust-analyzer/5ecace48f693afaa6adf8cb23086b651db3aec96' (2024-03-16) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/4fddc9be4eaf195d631333908f2a454b03628ee5' (2024-01-25) → 'github:nixos/nixpkgs/34ad8c9f29a18b4dd97a9ad40ceb16954f24afe7' (2024-03-17) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/f56597d53fd174f796b5a7d3ee0b494f9e2285cc' (2024-01-20) → 'github:cachix/pre-commit-hooks.nix/5df5a70ad7575f6601d91f0efec95dd9bc619431' (2024-02-15) • Updated input 'procfile-nix': 'github:getchoo/procfile-nix/31a33e4264e5c6214844993c5b508fb3500ef5cd' (2024-01-27) → 'github:getchoo/procfile-nix/7a0ab379a4ab71c9deccaca9fb463e9aaea363d8' (2024-03-14) --------- Co-authored-by: seth <getchoo@tuta.io>
This commit is contained in:
parent
1ea08671fb
commit
9d0c022c68
38 changed files with 492 additions and 296 deletions
|
@ -41,3 +41,9 @@ tokio = { version = "1.35.1", features = [
|
||||||
"signal",
|
"signal",
|
||||||
] }
|
] }
|
||||||
url = { version = "2.5.0", features = ["serde"] }
|
url = { version = "2.5.0", features = ["serde"] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "s"
|
||||||
|
panic = "abort"
|
||||||
|
strip = "symbols"
|
||||||
|
|
63
build.rs
63
build.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
|
@ -9,14 +10,15 @@ include!("src/tags.rs");
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let generated = Path::new(&out_dir).join("generated.rs");
|
let dest_file = Path::new(&out_dir).join("generated.rs");
|
||||||
|
let mut file = fs::File::create(dest_file).unwrap();
|
||||||
|
|
||||||
let tag_files: Vec<String> = fs::read_dir(TAG_DIR)
|
let tag_files: Vec<String> = fs::read_dir(TAG_DIR)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|f| f.unwrap().file_name().to_string_lossy().to_string())
|
.map(|f| f.unwrap().file_name().to_string_lossy().to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tags: Vec<Tag> = tag_files
|
let mut tags: Vec<Tag> = tag_files
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
|
@ -39,50 +41,45 @@ fn main() {
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
content,
|
content,
|
||||||
file_name: name,
|
id: name.trim_end_matches(".md").to_string(),
|
||||||
frontmatter: data,
|
frontmatter: data,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let formatted_names: Vec<String> = tags
|
tags.sort_by(|t1, t2| t1.id.cmp(&t2.id));
|
||||||
|
|
||||||
|
let tag_names: Vec<String> = tags.iter().map(|t| format!("{},", t.id)).collect();
|
||||||
|
|
||||||
|
let tag_matches: Vec<String> = tags
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.file_name.replace(".md", "").replace('-', "_"))
|
.map(|t| format!("Self::{} => \"{}\",", t.id, t.id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tag_choice = format!(
|
writeln!(
|
||||||
r#"
|
file,
|
||||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
r#"#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Debug, poise::ChoiceParameter)]
|
#[derive(Clone, Debug, poise::ChoiceParameter)]
|
||||||
pub enum Choice {{
|
pub enum Choice {{
|
||||||
{}
|
{}
|
||||||
}}"#,
|
}}"#,
|
||||||
formatted_names.join(",\n")
|
tag_names.join("\n")
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let to_str = format!(
|
writeln!(
|
||||||
r#"
|
file,
|
||||||
impl Choice {{
|
r#"impl Choice {{
|
||||||
fn as_str(&self) -> &str {{
|
fn as_str(&self) -> &str {{
|
||||||
match &self {{
|
match &self {{
|
||||||
{}
|
{}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
}}
|
}}"#,
|
||||||
"#,
|
tag_matches.join("\n")
|
||||||
formatted_names
|
)
|
||||||
.iter()
|
.unwrap();
|
||||||
.map(|n| {
|
|
||||||
let file_name = n.replace('_', "-") + ".md";
|
|
||||||
format!("Self::{n} => \"{file_name}\",")
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
);
|
|
||||||
|
|
||||||
let contents = Vec::from([tag_choice, to_str]).join("\n\n");
|
|
||||||
|
|
||||||
fs::write(generated, contents).unwrap();
|
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-env=TAGS={}",
|
"cargo:rustc-env=TAGS={}",
|
||||||
// make sure we can deserialize with env! at runtime
|
// make sure we can deserialize with env! at runtime
|
||||||
|
|
95
flake.lock
generated
95
flake.lock
generated
|
@ -8,11 +8,11 @@
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706336364,
|
"lastModified": 1710656467,
|
||||||
"narHash": "sha256-mJ5i2YIVKv6jTN2+l3oOUUej2NUVjJX/H3bAq6019ks=",
|
"narHash": "sha256-4Plj0vNP+ckWVNi6EtVojL9YV2dwSH7H4UMFCV40VE8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "eb683549b7d76b12d1a009f888b91b70ed34485f",
|
"rev": "c53bb4a32f2fce7acf4e8e160a54779c4460ffdb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -37,6 +37,26 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709336216,
|
||||||
|
"narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
|
@ -98,11 +118,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706173671,
|
"lastModified": 1710701596,
|
||||||
"narHash": "sha256-lciR7kQUK2FCAYuszyd7zyRRmTaXVeoZsCyK6QFpGdk=",
|
"narHash": "sha256-v4lsAi3vE/sEWg0G8AydMjs3NTHlsNw8K01xw06cKLg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4fddc9be4eaf195d631333908f2a454b03628ee5",
|
"rev": "34ad8c9f29a18b4dd97a9ad40ceb16954f24afe7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -128,26 +148,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parts": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1704982712,
|
|
||||||
"narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "07f6395285469419cf9d078f59b5b49993198c00",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
"pre-commit-hooks": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
|
@ -159,11 +159,11 @@
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705757126,
|
"lastModified": 1708018599,
|
||||||
"narHash": "sha256-Eksr+n4Q8EYZKAN0Scef5JK4H6FcHc+TKNHb95CWm+c=",
|
"narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "f56597d53fd174f796b5a7d3ee0b494f9e2285cc",
|
"rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -179,11 +179,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706387387,
|
"lastModified": 1710407041,
|
||||||
"narHash": "sha256-7C3HncC25yK1kvLp+/9KoBa1Iz5Ly2JtICqmCz2nvio=",
|
"narHash": "sha256-rCHklFHPzrq341KoTgXNdknNZbjOJ+VmalqX5s5YdGM=",
|
||||||
"owner": "getchoo",
|
"owner": "getchoo",
|
||||||
"repo": "procfile-nix",
|
"repo": "procfile-nix",
|
||||||
"rev": "31a33e4264e5c6214844993c5b508fb3500ef5cd",
|
"rev": "7a0ab379a4ab71c9deccaca9fb463e9aaea363d8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -195,21 +195,22 @@
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fenix": "fenix",
|
"fenix": "fenix",
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
"naersk": "naersk",
|
"naersk": "naersk",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"parts": "parts",
|
|
||||||
"pre-commit-hooks": "pre-commit-hooks",
|
"pre-commit-hooks": "pre-commit-hooks",
|
||||||
"procfile-nix": "procfile-nix"
|
"procfile-nix": "procfile-nix",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706295183,
|
"lastModified": 1710610549,
|
||||||
"narHash": "sha256-VSyMaUsXfjb31B8/uT5cM5qXC1VOHLVsCi/bQuo3O/g=",
|
"narHash": "sha256-xFIGLn5u+msUazlLbdjZ3gQgXrt7Lrlhq+XXUH0XU/0=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "596e5c77cf5b2b660b3ac2ce732fa0596c246d9b",
|
"rev": "5ecace48f693afaa6adf8cb23086b651db3aec96",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -233,6 +234,26 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710278050,
|
||||||
|
"narHash": "sha256-Oc6BP7soXqb8itlHI8UKkdf3V9GeJpa1S39SR5+HJys=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "35791f76524086ab4b785a33e4abbedfda64bd22",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
12
flake.nix
12
flake.nix
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
parts = {
|
flake-parts = {
|
||||||
url = "github:hercules-ci/flake-parts";
|
url = "github:hercules-ci/flake-parts";
|
||||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
@ -27,10 +27,15 @@
|
||||||
url = "github:getchoo/procfile-nix";
|
url = "github:getchoo/procfile-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
treefmt-nix = {
|
||||||
|
url = "github:numtide/treefmt-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {parts, ...} @ inputs:
|
outputs = {flake-parts, ...} @ inputs:
|
||||||
parts.lib.mkFlake {inherit inputs;} {
|
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||||
imports = [
|
imports = [
|
||||||
./nix/dev.nix
|
./nix/dev.nix
|
||||||
./nix/packages.nix
|
./nix/packages.nix
|
||||||
|
@ -38,6 +43,7 @@
|
||||||
|
|
||||||
inputs.pre-commit-hooks.flakeModule
|
inputs.pre-commit-hooks.flakeModule
|
||||||
inputs.procfile-nix.flakeModule
|
inputs.procfile-nix.flakeModule
|
||||||
|
inputs.treefmt-nix.flakeModule
|
||||||
];
|
];
|
||||||
|
|
||||||
systems = [
|
systems = [
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
self,
|
flake-parts-lib,
|
||||||
|
withSystem,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
flake.nixosModules.default = import ./module.nix self;
|
flake.nixosModules.default = flake-parts-lib.importApply ./module.nix {
|
||||||
|
inherit withSystem;
|
||||||
|
};
|
||||||
|
|
||||||
perSystem = {
|
perSystem = {
|
||||||
lib,
|
lib,
|
||||||
|
@ -13,8 +16,8 @@
|
||||||
inputs',
|
inputs',
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
crossPkgsFor =
|
crossPkgs =
|
||||||
{
|
rec {
|
||||||
x86_64-linux = {
|
x86_64-linux = {
|
||||||
x86_64 = pkgs.pkgsStatic;
|
x86_64 = pkgs.pkgsStatic;
|
||||||
aarch64 = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic;
|
aarch64 = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic;
|
||||||
|
@ -30,11 +33,13 @@
|
||||||
aarch64 = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic;
|
aarch64 = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic;
|
||||||
};
|
};
|
||||||
|
|
||||||
aarch64-darwin = crossPkgsFor.x86_64-darwin;
|
aarch64-darwin = x86_64-darwin;
|
||||||
}
|
}
|
||||||
.${system};
|
.${system};
|
||||||
|
|
||||||
exeFor = arch: let
|
refractionFor = arch: let
|
||||||
|
inherit (crossPkgs.${arch}.stdenv) cc;
|
||||||
|
|
||||||
target = "${arch}-unknown-linux-musl";
|
target = "${arch}-unknown-linux-musl";
|
||||||
target' = builtins.replaceStrings ["-"] ["_"] target;
|
target' = builtins.replaceStrings ["-"] ["_"] target;
|
||||||
targetUpper = lib.toUpper target';
|
targetUpper = lib.toUpper target';
|
||||||
|
@ -52,8 +57,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
refraction = self'.packages.refraction.override {
|
refraction = self'.packages.refraction.override {
|
||||||
|
lto = true;
|
||||||
naersk = naersk';
|
naersk = naersk';
|
||||||
optimizeSize = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
newAttrs = {
|
newAttrs = {
|
||||||
|
@ -62,26 +67,26 @@
|
||||||
"CARGO_TARGET_${targetUpper}_RUSTFLAGS" = "-C target-feature=+crt-static";
|
"CARGO_TARGET_${targetUpper}_RUSTFLAGS" = "-C target-feature=+crt-static";
|
||||||
"CARGO_TARGET_${targetUpper}_LINKER" = newAttrs."CC_${target'}";
|
"CARGO_TARGET_${targetUpper}_LINKER" = newAttrs."CC_${target'}";
|
||||||
};
|
};
|
||||||
|
|
||||||
inherit (crossPkgsFor.${arch}.stdenv) cc;
|
|
||||||
in
|
in
|
||||||
lib.getExe (
|
refraction.overrideAttrs newAttrs;
|
||||||
refraction.overrideAttrs (lib.const newAttrs)
|
|
||||||
);
|
|
||||||
|
|
||||||
containerFor = arch:
|
containerFor = arch:
|
||||||
pkgs.dockerTools.buildLayeredImage {
|
pkgs.dockerTools.buildLayeredImage {
|
||||||
name = "refraction";
|
name = "refraction";
|
||||||
tag = "latest-${arch}";
|
tag = "latest-${arch}";
|
||||||
contents = [pkgs.dockerTools.caCertificates];
|
contents = [pkgs.dockerTools.caCertificates];
|
||||||
config.Cmd = [(exeFor arch)];
|
config.Cmd = [
|
||||||
|
(lib.getExe (refractionFor arch))
|
||||||
|
];
|
||||||
|
|
||||||
architecture = crossPkgsFor.${arch}.go.GOARCH;
|
architecture = crossPkgs.${arch}.go.GOARCH;
|
||||||
};
|
};
|
||||||
in {
|
|
||||||
legacyPackages = {
|
mkPackagesFor = arch: {
|
||||||
container-x86_64 = containerFor "x86_64";
|
"refraction-static-${arch}" = refractionFor arch;
|
||||||
container-aarch64 = containerFor "aarch64";
|
"container-${arch}" = containerFor arch;
|
||||||
};
|
};
|
||||||
|
in {
|
||||||
|
legacyPackages = lib.attrsets.mergeAttrsList (map mkPackagesFor ["x86_64" "aarch64"]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
lib,
|
lib,
|
||||||
stdenv,
|
stdenv,
|
||||||
naersk,
|
naersk,
|
||||||
CoreFoundation,
|
darwin,
|
||||||
Security,
|
|
||||||
SystemConfiguration,
|
|
||||||
version,
|
version,
|
||||||
optimizeSize ? false,
|
lto ? false,
|
||||||
}:
|
}:
|
||||||
naersk.buildPackage {
|
naersk.buildPackage {
|
||||||
pname = "refraction";
|
pname = "refraction";
|
||||||
|
@ -23,13 +21,13 @@ naersk.buildPackage {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
buildInputs = lib.optionals stdenv.hostPlatform.isDarwin [
|
buildInputs = lib.optionals stdenv.hostPlatform.isDarwin (with darwin.apple_sdk.frameworks; [
|
||||||
CoreFoundation
|
CoreFoundation
|
||||||
Security
|
Security
|
||||||
SystemConfiguration
|
SystemConfiguration
|
||||||
];
|
]);
|
||||||
|
|
||||||
cargoBuildFlags = lib.optionals optimizeSize ["-C" "codegen-units=1" "-C" "strip=symbols" "-C" "opt-level=z"];
|
cargoBuildFlags = lib.optionals lto ["-C" "lto=thin" "-C" "embed-bitcode=yes" "-Zdylib-lto"];
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
mainProgram = "refraction";
|
mainProgram = "refraction";
|
||||||
|
|
56
nix/dev.nix
56
nix/dev.nix
|
@ -5,24 +5,9 @@
|
||||||
config,
|
config,
|
||||||
self',
|
self',
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
pre-commit.settings.hooks = {
|
enableAll = lib.flip lib.genAttrs (lib.const {enable = true;});
|
||||||
actionlint.enable = true;
|
in {
|
||||||
alejandra.enable = true;
|
|
||||||
deadnix.enable = true;
|
|
||||||
rustfmt.enable = true;
|
|
||||||
statix.enable = true;
|
|
||||||
nil.enable = true;
|
|
||||||
prettier = {
|
|
||||||
enable = true;
|
|
||||||
excludes = ["flake.lock"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
procfiles.daemons.processes = {
|
|
||||||
redis = lib.getExe' pkgs.redis "redis-server";
|
|
||||||
};
|
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
${config.pre-commit.installationScript}
|
${config.pre-commit.installationScript}
|
||||||
|
@ -31,6 +16,7 @@
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
# general
|
# general
|
||||||
actionlint
|
actionlint
|
||||||
|
nodePackages.prettier
|
||||||
config.procfiles.daemons.package
|
config.procfiles.daemons.package
|
||||||
|
|
||||||
# rust
|
# rust
|
||||||
|
@ -50,6 +36,38 @@
|
||||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||||
};
|
};
|
||||||
|
|
||||||
formatter = pkgs.alejandra;
|
treefmt = {
|
||||||
|
projectRootFile = "flake.nix";
|
||||||
|
|
||||||
|
programs = enableAll [
|
||||||
|
"alejandra"
|
||||||
|
"deadnix"
|
||||||
|
"prettier"
|
||||||
|
"rustfmt"
|
||||||
|
];
|
||||||
|
|
||||||
|
settings.global = {
|
||||||
|
excludes = [
|
||||||
|
"./target"
|
||||||
|
"./flake.lock"
|
||||||
|
"./Cargo.lock"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pre-commit.settings = {
|
||||||
|
settings.treefmt.package = config.treefmt.build.wrapper;
|
||||||
|
|
||||||
|
hooks = enableAll [
|
||||||
|
"actionlint"
|
||||||
|
"nil"
|
||||||
|
"statix"
|
||||||
|
"treefmt"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
procfiles.daemons.processes = {
|
||||||
|
redis = lib.getExe' pkgs.redis "redis-server";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
self: {
|
{withSystem, ...}: {
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
@ -22,7 +22,9 @@ self: {
|
||||||
in {
|
in {
|
||||||
options.services.refraction = {
|
options.services.refraction = {
|
||||||
enable = mkEnableOption "refraction";
|
enable = mkEnableOption "refraction";
|
||||||
package = mkPackageOption self.packages.${pkgs.stdenv.hostPlatform.system} "refraction" {};
|
package = mkPackageOption (
|
||||||
|
withSystem pkgs.stdenv.hostPlatform.system ({pkgs, ...}: pkgs)
|
||||||
|
) "refraction" {};
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
description = mdDoc ''
|
description = mdDoc ''
|
||||||
|
@ -102,7 +104,7 @@ in {
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
Restart = "always";
|
Restart = "on-failure";
|
||||||
|
|
||||||
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
|
||||||
|
|
||||||
|
@ -122,8 +124,14 @@ in {
|
||||||
ProtectKernelModules = true;
|
ProtectKernelModules = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectKernelTunables = true;
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
RestrictNamespaces = "uts ipc pid user cgroup";
|
RestrictNamespaces = true;
|
||||||
RestrictSUIDSGID = true;
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@resources"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,6 @@
|
||||||
packages = {
|
packages = {
|
||||||
refraction = pkgs.callPackage ./derivation.nix {
|
refraction = pkgs.callPackage ./derivation.nix {
|
||||||
version = builtins.substring 0 7 self.rev or "dirty";
|
version = builtins.substring 0 7 self.rev or "dirty";
|
||||||
|
|
||||||
inherit
|
|
||||||
(pkgs.darwin.apple_sdk.frameworks)
|
|
||||||
CoreFoundation
|
|
||||||
Security
|
|
||||||
SystemConfiguration
|
|
||||||
;
|
|
||||||
|
|
||||||
naersk = inputs.naersk.lib.${system};
|
naersk = inputs.naersk.lib.${system};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,10 @@ use reqwest::StatusCode;
|
||||||
const DADJOKE: &str = "https://icanhazdadjoke.com";
|
const DADJOKE: &str = "https://icanhazdadjoke.com";
|
||||||
|
|
||||||
pub async fn get_joke() -> Result<String> {
|
pub async fn get_joke() -> Result<String> {
|
||||||
let req = REQWEST_CLIENT.get(DADJOKE).build()?;
|
let req = REQWEST_CLIENT
|
||||||
|
.get(DADJOKE)
|
||||||
|
.header("Accept", "text/plain")
|
||||||
|
.build()?;
|
||||||
|
|
||||||
debug!("Making request to {}", req.url());
|
debug!("Making request to {}", req.url());
|
||||||
let resp = REQWEST_CLIENT.execute(req).await?;
|
let resp = REQWEST_CLIENT.execute(req).await?;
|
||||||
|
|
23
src/commands/general/help.rs
Normal file
23
src/commands/general/help.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use eyre::Result;
|
||||||
|
use poise::{builtins, samples::HelpConfiguration};
|
||||||
|
|
||||||
|
use crate::Context;
|
||||||
|
|
||||||
|
/// View the help menu
|
||||||
|
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
||||||
|
pub async fn help(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "provide information about a specific command"] command: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
builtins::help(
|
||||||
|
ctx,
|
||||||
|
command.as_deref(),
|
||||||
|
HelpConfiguration {
|
||||||
|
extra_text_at_bottom: "Use /help for more information about a specific command!",
|
||||||
|
..HelpConfiguration::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -5,11 +5,14 @@ use eyre::Result;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
/// It's a joke
|
/// It's a joke
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
||||||
pub async fn joke(ctx: Context<'_>) -> Result<()> {
|
pub async fn joke(ctx: Context<'_>) -> Result<()> {
|
||||||
trace!("Running joke command");
|
trace!("Running joke command");
|
||||||
|
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
let joke = dadjoke::get_joke().await?;
|
let joke = dadjoke::get_joke().await?;
|
||||||
|
|
||||||
ctx.reply(joke).await?;
|
ctx.say(joke).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,34 @@
|
||||||
use crate::{consts, Context};
|
use crate::{consts, Context};
|
||||||
|
|
||||||
use eyre::{OptionExt, Result};
|
use eyre::{eyre, Context as _, OptionExt, Result};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use poise::serenity_prelude::CreateEmbed;
|
use poise::serenity_prelude::CreateEmbed;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
|
||||||
/// Returns the number of members in the server
|
/// Returns the number of members in the server
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(slash_command, prefix_command, guild_only = true, track_edits = true)]
|
||||||
pub async fn members(ctx: Context<'_>) -> Result<()> {
|
pub async fn members(ctx: Context<'_>) -> Result<()> {
|
||||||
trace!("Running members command");
|
trace!("Running members command");
|
||||||
let guild = ctx.guild().ok_or_eyre("Couldn't fetch guild!")?.to_owned();
|
|
||||||
|
|
||||||
let count = guild.member_count;
|
ctx.defer().await?;
|
||||||
let online = if let Some(count) = guild.approximate_presence_count {
|
|
||||||
count.to_string()
|
let guild_id = ctx.guild_id().ok_or_eyre("Couldn't get guild ID!")?;
|
||||||
} else {
|
let guild = ctx
|
||||||
"Undefined".to_string()
|
.http()
|
||||||
};
|
.get_guild_with_counts(guild_id)
|
||||||
|
.await
|
||||||
|
.wrap_err_with(|| format!("Couldn't fetch guild {guild_id} with counts!"))?;
|
||||||
|
|
||||||
|
let member_count = guild
|
||||||
|
.approximate_member_count
|
||||||
|
.ok_or_else(|| eyre!("Couldn't get member count for guild {guild_id}!"))?;
|
||||||
|
let online_count = guild
|
||||||
|
.approximate_presence_count
|
||||||
|
.ok_or_else(|| eyre!("Couldn't get online count for guild {guild_id}!"))?;
|
||||||
|
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
.title(format!("{count} total members!"))
|
.title(format!("{member_count} total members!",))
|
||||||
.description(format!("{online} online members"))
|
.description(format!("{online_count} online members",))
|
||||||
.color(consts::COLORS["blue"]);
|
.color(consts::COLORS["blue"]);
|
||||||
let reply = CreateReply::default().embed(embed);
|
let reply = CreateReply::default().embed(embed);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod help;
|
||||||
mod joke;
|
mod joke;
|
||||||
mod members;
|
mod members;
|
||||||
mod ping;
|
mod ping;
|
||||||
|
@ -6,6 +7,7 @@ mod say;
|
||||||
mod stars;
|
mod stars;
|
||||||
mod tag;
|
mod tag;
|
||||||
|
|
||||||
|
pub use help::help;
|
||||||
pub use joke::joke;
|
pub use joke::joke;
|
||||||
pub use members::members;
|
pub use members::members;
|
||||||
pub use ping::ping;
|
pub use ping::ping;
|
||||||
|
|
|
@ -4,9 +4,9 @@ use eyre::Result;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
/// Replies with pong!
|
/// Replies with pong!
|
||||||
#[poise::command(slash_command, prefix_command, ephemeral)]
|
#[poise::command(slash_command, prefix_command, track_edits = true, ephemeral)]
|
||||||
pub async fn ping(ctx: Context<'_>) -> Result<()> {
|
pub async fn ping(ctx: Context<'_>) -> Result<()> {
|
||||||
trace!("Running ping command!");
|
trace!("Running ping command!");
|
||||||
ctx.reply("Pong!").await?;
|
ctx.say("Pong!").await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,15 @@ use poise::serenity_prelude::{CreateEmbed, CreateEmbedFooter};
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
|
||||||
/// Gets a Rory photo!
|
/// Gets a Rory photo!
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
||||||
pub async fn rory(
|
pub async fn rory(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "specify a Rory ID"] id: Option<u64>,
|
#[description = "specify a Rory ID"] id: Option<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
trace!("Running rory command");
|
trace!("Running rory command");
|
||||||
|
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
let rory = rory::get(id).await?;
|
let rory = rory::get(id).await?;
|
||||||
|
|
||||||
let embed = {
|
let embed = {
|
||||||
|
|
|
@ -9,18 +9,30 @@ use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateMessage};
|
||||||
prefix_command,
|
prefix_command,
|
||||||
ephemeral,
|
ephemeral,
|
||||||
default_member_permissions = "MODERATE_MEMBERS",
|
default_member_permissions = "MODERATE_MEMBERS",
|
||||||
required_permissions = "MODERATE_MEMBERS"
|
required_permissions = "MODERATE_MEMBERS",
|
||||||
|
guild_only = true
|
||||||
)]
|
)]
|
||||||
pub async fn say(ctx: Context<'_>, #[description = "Just content?"] content: String) -> Result<()> {
|
pub async fn say(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "the message content"] content: String,
|
||||||
|
) -> Result<()> {
|
||||||
let guild = ctx.guild().ok_or_eyre("Couldn't get guild!")?.to_owned();
|
let guild = ctx.guild().ok_or_eyre("Couldn't get guild!")?.to_owned();
|
||||||
let channel = ctx
|
let channel = ctx
|
||||||
.guild_channel()
|
.guild_channel()
|
||||||
.await
|
.await
|
||||||
.ok_or_eyre("Couldn't get channel!")?;
|
.ok_or_eyre("Couldn't get channel!")?;
|
||||||
|
|
||||||
|
if let Context::Prefix(prefix) = ctx {
|
||||||
|
// ignore error, we might not have perm
|
||||||
|
let _ = prefix.msg.delete(ctx).await;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.defer_ephemeral().await?;
|
ctx.defer_ephemeral().await?;
|
||||||
channel.say(ctx, &content).await?;
|
channel.say(ctx, &content).await?;
|
||||||
ctx.say("I said what you said!").await?;
|
|
||||||
|
if let Context::Application(_) = ctx {
|
||||||
|
ctx.say("I said what you said!").await?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(channel_id) = ctx.data().config.discord.channels().say_log_channel_id() {
|
if let Some(channel_id) = ctx.data().config.discord.channels().say_log_channel_id() {
|
||||||
let log_channel = guild
|
let log_channel = guild
|
||||||
|
@ -29,8 +41,11 @@ pub async fn say(ctx: Context<'_>, #[description = "Just content?"] content: Str
|
||||||
.find(|c| c.0 == &channel_id)
|
.find(|c| c.0 == &channel_id)
|
||||||
.ok_or_eyre("Couldn't get log channel from guild!")?;
|
.ok_or_eyre("Couldn't get log channel from guild!")?;
|
||||||
|
|
||||||
let author = CreateEmbedAuthor::new(ctx.author().tag())
|
let author = CreateEmbedAuthor::new(ctx.author().tag()).icon_url(
|
||||||
.icon_url(ctx.author().avatar_url().unwrap_or("Undefined".to_string()));
|
ctx.author()
|
||||||
|
.avatar_url()
|
||||||
|
.unwrap_or_else(|| ctx.author().default_avatar_url()),
|
||||||
|
);
|
||||||
|
|
||||||
let embed = CreateEmbed::default()
|
let embed = CreateEmbed::default()
|
||||||
.title("Say command used!")
|
.title("Say command used!")
|
||||||
|
|
|
@ -6,10 +6,12 @@ use poise::serenity_prelude::CreateEmbed;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
|
||||||
/// Returns GitHub stargazer count
|
/// Returns GitHub stargazer count
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
||||||
pub async fn stars(ctx: Context<'_>) -> Result<()> {
|
pub async fn stars(ctx: Context<'_>) -> Result<()> {
|
||||||
trace!("Running stars command");
|
trace!("Running stars command");
|
||||||
|
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
let prismlauncher = ctx
|
let prismlauncher = ctx
|
||||||
.data()
|
.data()
|
||||||
.octocrab
|
.octocrab
|
||||||
|
|
|
@ -13,19 +13,24 @@ include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
static TAGS: Lazy<Vec<Tag>> = Lazy::new(|| serde_json::from_str(env!("TAGS")).unwrap());
|
static TAGS: Lazy<Vec<Tag>> = Lazy::new(|| serde_json::from_str(env!("TAGS")).unwrap());
|
||||||
|
|
||||||
/// Send a tag
|
/// Send a tag
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
prefix_command,
|
||||||
|
track_edits = true,
|
||||||
|
help_text_fn = help
|
||||||
|
)]
|
||||||
pub async fn tag(
|
pub async fn tag(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "the copypasta you want to send"] name: Choice,
|
#[description = "the tag to send"] name: Choice,
|
||||||
user: Option<User>,
|
#[description = "a user to mention"] user: Option<User>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
trace!("Running tag command");
|
trace!("Running tag command");
|
||||||
|
|
||||||
let tag_file = name.as_str();
|
let tag_id = name.as_str();
|
||||||
let tag = TAGS
|
let tag = TAGS
|
||||||
.iter()
|
.iter()
|
||||||
.find(|t| t.file_name == tag_file)
|
.find(|t| t.id == tag_id)
|
||||||
.ok_or_else(|| eyre!("Tried to get non-existent tag: {tag_file}"))?;
|
.ok_or_else(|| eyre!("Tried to get non-existent tag: {tag_id}"))?;
|
||||||
|
|
||||||
let frontmatter = &tag.frontmatter;
|
let frontmatter = &tag.frontmatter;
|
||||||
|
|
||||||
|
@ -49,6 +54,9 @@ pub async fn tag(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e = e.title(&frontmatter.title);
|
||||||
|
e = e.description(&tag.content);
|
||||||
|
|
||||||
e
|
e
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,3 +74,13 @@ pub async fn tag(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn help() -> String {
|
||||||
|
let tag_list = TAGS
|
||||||
|
.iter()
|
||||||
|
.map(|tag| format!("`{}`", tag.id))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
format!("Available tags: {tag_list}")
|
||||||
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ pub fn get() -> Vec<Command<Data, Report>> {
|
||||||
general::say(),
|
general::say(),
|
||||||
general::stars(),
|
general::stars(),
|
||||||
general::tag(),
|
general::tag(),
|
||||||
|
general::help(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
use crate::consts;
|
use crate::consts;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use eyre::Report;
|
use eyre::Report;
|
||||||
use log::error;
|
use log::error;
|
||||||
use poise::serenity_prelude::{CreateEmbed, Timestamp};
|
use poise::serenity_prelude::{CreateEmbed, Timestamp};
|
||||||
use poise::{CreateReply, FrameworkError};
|
use poise::{CreateReply, FrameworkError};
|
||||||
|
|
||||||
|
// getchoo: i like writeln! and don't like
|
||||||
|
macro_rules! writelne {
|
||||||
|
($dst:expr, $($arg:tt)*) => {
|
||||||
|
if let Err(why) = writeln!($dst, $($arg)*) {
|
||||||
|
error!("We somehow cannot write to what should be on the heap. What are you using this macro with? Anyways, here's the error:\n{why:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
||||||
match error {
|
match error {
|
||||||
FrameworkError::Setup {
|
FrameworkError::Setup {
|
||||||
|
@ -44,6 +54,42 @@ pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameworkError::ArgumentParse {
|
||||||
|
error, input, ctx, ..
|
||||||
|
} => {
|
||||||
|
let mut response = String::new();
|
||||||
|
|
||||||
|
if let Some(input) = input {
|
||||||
|
writelne!(
|
||||||
|
&mut response,
|
||||||
|
"**Cannot parse `{input}` as argument: {error}**\n"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
writelne!(&mut response, "**{error}**\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(help_text) = ctx.command().help_text.as_ref() {
|
||||||
|
writelne!(&mut response, "{help_text}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.command().invoke_on_edit {
|
||||||
|
writelne!(
|
||||||
|
&mut response,
|
||||||
|
"**Tip:** Edit your message to update the response."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
writelne!(
|
||||||
|
&mut response,
|
||||||
|
"For more information, refer to /help {}.",
|
||||||
|
ctx.command().name
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(why) = ctx.say(response).await {
|
||||||
|
error!("Unhandled error displaying ArgumentParse error\n{why:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
error => {
|
error => {
|
||||||
if let Err(e) = poise::builtins::on_error(error).await {
|
if let Err(e) = poise::builtins::on_error(error).await {
|
||||||
error!("Unhandled error occurred:\n{e:#?}");
|
error!("Unhandled error occurred:\n{e:#?}");
|
||||||
|
|
|
@ -95,7 +95,7 @@ fn intel_hd(log: &str) -> Issue {
|
||||||
See https://prismlauncher.org/wiki/getting-started/installing-java/#a-note-about-intel-hd-20003000-on-windows-10 for more info".to_string()
|
See https://prismlauncher.org/wiki/getting-started/installing-java/#a-note-about-intel-hd-20003000-on-windows-10 for more info".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.<init>(Ljava/util/jar/Manifest;)V");
|
let found = log.contains("org.lwjgl.LWJGLException: Pixel format not accelerated");
|
||||||
found.then_some(issue)
|
found.then_some(issue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ async fn outdated_launcher(log: &str, data: &Data) -> Result<Issue> {
|
||||||
|
|
||||||
if version_from_log < latest_version {
|
if version_from_log < latest_version {
|
||||||
let issue = (
|
let issue = (
|
||||||
"Outdated Prism Launcher".to_string(),
|
"Outdated Prism Launcher".to_string(),
|
||||||
format!("Your installed version is {version_from_log}, while the newest version is {latest_version}.\nPlease update, for more info see https://prismlauncher.org/download/")
|
format!("Your installed version is {version_from_log}, while the newest version is {latest_version}.\nPlease update, for more info see https://prismlauncher.org/download/")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
||||||
|
|
||||||
if log.is_err() {
|
if log.is_err() {
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
.title("Analyze failed!")
|
.title("Analysis failed!")
|
||||||
.description("Couldn't download log");
|
.description("Couldn't download log");
|
||||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
||||||
let our_message = CreateMessage::new()
|
let our_message = CreateMessage::new()
|
||||||
|
@ -43,11 +43,9 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
||||||
let mut e = CreateEmbed::new().title("Log analysis");
|
let mut e = CreateEmbed::new().title("Log analysis");
|
||||||
|
|
||||||
if issues.is_empty() {
|
if issues.is_empty() {
|
||||||
e = e.color(COLORS["green"]).field(
|
e = e
|
||||||
"Analyze failed!",
|
.color(COLORS["green"])
|
||||||
"No issues found automatically",
|
.description("No issues found automatically");
|
||||||
false,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
e = e.color(COLORS["red"]);
|
e = e.color(COLORS["red"]);
|
||||||
|
|
||||||
|
@ -61,6 +59,7 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
||||||
|
|
||||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
||||||
let message = CreateMessage::new()
|
let message = CreateMessage::new()
|
||||||
|
.reference_message(message)
|
||||||
.allowed_mentions(allowed_mentions)
|
.allowed_mentions(allowed_mentions)
|
||||||
.embed(embed);
|
.embed(embed);
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,12 @@ use poise::serenity_prelude::{Context, CreateAllowedMentions, CreateMessage, Mes
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
||||||
let embeds = utils::resolve_message(ctx, message).await?;
|
let embeds = utils::resolve_message::from_message(ctx, message).await?;
|
||||||
|
|
||||||
// TODO getchoo: actually reply to user
|
|
||||||
// ...not sure why Message doesn't give me a builder in reply() or equivalents
|
|
||||||
if !embeds.is_empty() {
|
if !embeds.is_empty() {
|
||||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);
|
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);
|
||||||
let reply = CreateMessage::new()
|
let reply = CreateMessage::new()
|
||||||
|
.reference_message(message)
|
||||||
.embeds(embeds)
|
.embeds(embeds)
|
||||||
.allowed_mentions(allowed_mentions);
|
.allowed_mentions(allowed_mentions);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod support_onboard;
|
||||||
pub async fn handle(
|
pub async fn handle(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
event: &FullEvent,
|
event: &FullEvent,
|
||||||
framework: FrameworkContext<'_, Data, Report>,
|
_framework: FrameworkContext<'_, Data, Report>,
|
||||||
data: &Data,
|
data: &Data,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match event {
|
match event {
|
||||||
|
@ -31,8 +31,10 @@ pub async fn handle(
|
||||||
|
|
||||||
FullEvent::Message { new_message } => {
|
FullEvent::Message { new_message } => {
|
||||||
// ignore new messages from bots
|
// ignore new messages from bots
|
||||||
// NOTE: the webhook_id check allows us to still respond to PK users
|
// note: the webhook_id check allows us to still respond to PK users
|
||||||
if new_message.author.bot && new_message.webhook_id.is_none() {
|
if (new_message.author.bot && new_message.webhook_id.is_none())
|
||||||
|
|| new_message.is_own(ctx)
|
||||||
|
{
|
||||||
trace!("Ignoring message {} from bot", new_message.id);
|
trace!("Ignoring message {} from bot", new_message.id);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -57,7 +59,7 @@ pub async fn handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
FullEvent::ThreadCreate { thread } => {
|
FullEvent::ThreadCreate { thread } => {
|
||||||
support_onboard::handle(ctx, thread, framework).await?;
|
support_onboard::handle(ctx, thread).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -2,18 +2,18 @@ use crate::{api, Data};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::trace;
|
use log::{debug, trace};
|
||||||
use poise::serenity_prelude::{Context, Message};
|
use poise::serenity_prelude::{Context, Message};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
const PK_DELAY_SEC: Duration = Duration::from_secs(1000);
|
const PK_DELAY: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
pub async fn is_message_proxied(message: &Message) -> Result<bool> {
|
pub async fn is_message_proxied(message: &Message) -> Result<bool> {
|
||||||
trace!(
|
trace!(
|
||||||
"Waiting on PluralKit API for {} seconds",
|
"Waiting on PluralKit API for {} seconds",
|
||||||
PK_DELAY_SEC.as_secs()
|
PK_DELAY.as_secs()
|
||||||
);
|
);
|
||||||
sleep(PK_DELAY_SEC).await;
|
sleep(PK_DELAY).await;
|
||||||
|
|
||||||
let proxied = api::pluralkit::get_sender(message.id).await.is_ok();
|
let proxied = api::pluralkit::get_sender(message.id).await.is_ok();
|
||||||
|
|
||||||
|
@ -21,21 +21,23 @@ pub async fn is_message_proxied(message: &Message) -> Result<bool> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
|
pub async fn handle(_: &Context, msg: &Message, data: &Data) -> Result<()> {
|
||||||
if msg.webhook_id.is_some() {
|
if msg.webhook_id.is_none() {
|
||||||
trace!(
|
return Ok(());
|
||||||
"Message {} has a webhook ID. Checking if it was sent through PluralKit",
|
}
|
||||||
msg.id
|
|
||||||
);
|
|
||||||
|
|
||||||
trace!(
|
debug!(
|
||||||
"Waiting on PluralKit API for {} seconds",
|
"Message {} has a webhook ID. Checking if it was sent through PluralKit",
|
||||||
PK_DELAY_SEC.as_secs()
|
msg.id
|
||||||
);
|
);
|
||||||
sleep(PK_DELAY_SEC).await;
|
|
||||||
|
|
||||||
if let Ok(sender) = api::pluralkit::get_sender(msg.id).await {
|
trace!(
|
||||||
data.storage.store_user_plurality(sender).await?;
|
"Waiting on PluralKit API for {} seconds",
|
||||||
}
|
PK_DELAY.as_secs()
|
||||||
|
);
|
||||||
|
sleep(PK_DELAY).await;
|
||||||
|
|
||||||
|
if let Ok(sender) = api::pluralkit::get_sender(msg.id).await {
|
||||||
|
data.storage.store_user_plurality(sender).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,36 +1,17 @@
|
||||||
use crate::Data;
|
use eyre::{eyre, OptionExt, Result};
|
||||||
|
|
||||||
use eyre::{eyre, Context as _, OptionExt, Report, Result};
|
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
ChannelType, Context, CreateAllowedMentions, CreateMessage, GuildChannel,
|
ChannelType, Context, CreateAllowedMentions, CreateMessage, GuildChannel,
|
||||||
};
|
};
|
||||||
use poise::FrameworkContext;
|
|
||||||
|
|
||||||
pub async fn handle(
|
pub async fn handle(ctx: &Context, thread: &GuildChannel) -> Result<()> {
|
||||||
ctx: &Context,
|
|
||||||
thread: &GuildChannel,
|
|
||||||
framework: FrameworkContext<'_, Data, Report>,
|
|
||||||
) -> Result<()> {
|
|
||||||
if thread.kind != ChannelType::PublicThread {
|
if thread.kind != ChannelType::PublicThread {
|
||||||
trace!("Not doing support onboard in non-public thread channel");
|
trace!("Not doing support onboard in non-public thread channel");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @getchoo: it seems like we can get multiple ThreadCreate events
|
if thread.last_message_id.is_some() {
|
||||||
// should probably figure out a better way to not repeat ourselves here
|
debug!("Ignoring duplicate thread creation event");
|
||||||
if thread
|
|
||||||
.members(ctx)
|
|
||||||
.wrap_err_with(|| {
|
|
||||||
format!(
|
|
||||||
"Couldn't fetch members from thread {}! Not sending a support onboard message.",
|
|
||||||
thread.id
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.iter()
|
|
||||||
.any(|member| member.user.id == framework.bot_id)
|
|
||||||
{
|
|
||||||
debug!("Not sending support onboard message...I think i've been here before :p");
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{sync::Arc, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use eyre::{eyre, Context as _, Report, Result};
|
use eyre::{eyre, Context as _, Report, Result};
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
|
@ -111,7 +110,7 @@ async fn main() -> Result<()> {
|
||||||
},
|
},
|
||||||
|
|
||||||
prefix_options: PrefixFrameworkOptions {
|
prefix_options: PrefixFrameworkOptions {
|
||||||
prefix: Some("r".into()),
|
prefix: Some(".".into()),
|
||||||
edit_tracker: Some(Arc::from(EditTracker::for_timespan(Duration::from_secs(
|
edit_tracker: Some(Arc::from(EditTracker::for_timespan(Duration::from_secs(
|
||||||
3600,
|
3600,
|
||||||
)))),
|
)))),
|
||||||
|
|
|
@ -15,6 +15,6 @@ pub struct TagFrontmatter {
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub file_name: String,
|
pub id: String,
|
||||||
pub frontmatter: TagFrontmatter,
|
pub frontmatter: TagFrontmatter,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
mod resolve_message;
|
pub mod resolve_message;
|
||||||
|
|
||||||
pub use resolve_message::resolve as resolve_message;
|
|
||||||
|
|
|
@ -4,17 +4,14 @@ use eyre::{eyre, Context as _, Result};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
ChannelId, ChannelType, Colour, Context, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
|
Cache, CacheHttp, ChannelId, ChannelType, Colour, Context, CreateEmbed, CreateEmbedAuthor,
|
||||||
Message, MessageId,
|
CreateEmbedFooter, GuildChannel, Member, Message, MessageId, Permissions,
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
|
fn find_first_image(message: &Message) -> Option<String> {
|
||||||
Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<serverId>\d+)\/(?<channelId>\d+)\/(?<messageId>\d+)/g;").unwrap()
|
message
|
||||||
});
|
.attachments
|
||||||
|
|
||||||
fn find_first_image(msg: &Message) -> Option<String> {
|
|
||||||
msg.attachments
|
|
||||||
.iter()
|
.iter()
|
||||||
.find(|a| {
|
.find(|a| {
|
||||||
a.content_type
|
a.content_type
|
||||||
|
@ -25,86 +22,130 @@ fn find_first_image(msg: &Message) -> Option<String> {
|
||||||
.map(|res| res.url.clone())
|
.map(|res| res.url.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn resolve(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
async fn member_can_view_channel(
|
||||||
|
ctx: impl CacheHttp + AsRef<Cache>,
|
||||||
|
member: &Member,
|
||||||
|
channel: &GuildChannel,
|
||||||
|
) -> Result<bool> {
|
||||||
|
static REQUIRED_PERMISSIONS: Lazy<Permissions> =
|
||||||
|
Lazy::new(|| Permissions::VIEW_CHANNEL | Permissions::READ_MESSAGE_HISTORY);
|
||||||
|
|
||||||
|
let guild = ctx.http().get_guild(channel.guild_id).await?;
|
||||||
|
|
||||||
|
let channel_to_check = match &channel.kind {
|
||||||
|
ChannelType::PublicThread => {
|
||||||
|
let parent_id = channel
|
||||||
|
.parent_id
|
||||||
|
.ok_or_else(|| eyre!("Couldn't get parent of thread {}", channel.id))?;
|
||||||
|
parent_id
|
||||||
|
.to_channel(ctx)
|
||||||
|
.await?
|
||||||
|
.guild()
|
||||||
|
.ok_or_else(|| eyre!("Couldn't get GuildChannel from ChannelID {parent_id}!"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelType::Text | ChannelType::News => channel.to_owned(),
|
||||||
|
|
||||||
|
_ => return Ok(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
let can_view = guild
|
||||||
|
.user_permissions_in(&channel_to_check, member)
|
||||||
|
.contains(*REQUIRED_PERMISSIONS);
|
||||||
|
Ok(can_view)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn to_embed(
|
||||||
|
ctx: impl CacheHttp + AsRef<Cache>,
|
||||||
|
message: &Message,
|
||||||
|
) -> Result<CreateEmbed> {
|
||||||
|
let author = CreateEmbedAuthor::new(message.author.tag()).icon_url(
|
||||||
|
message
|
||||||
|
.author
|
||||||
|
.avatar_url()
|
||||||
|
.unwrap_or_else(|| message.author.default_avatar_url()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let footer = CreateEmbedFooter::new(format!(
|
||||||
|
"#{}",
|
||||||
|
message.channel(ctx).await?.guild().unwrap_or_default().name
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut embed = CreateEmbed::new()
|
||||||
|
.author(author)
|
||||||
|
.color(Colour::BLITZ_BLUE)
|
||||||
|
.timestamp(message.timestamp)
|
||||||
|
.footer(footer)
|
||||||
|
.description(format!(
|
||||||
|
"{}\n\n[Jump to original message]({})",
|
||||||
|
message.content,
|
||||||
|
message.link()
|
||||||
|
));
|
||||||
|
|
||||||
|
if !message.attachments.is_empty() {
|
||||||
|
embed = embed.fields(message.attachments.iter().map(|a| {
|
||||||
|
(
|
||||||
|
"Attachments".to_string(),
|
||||||
|
format!("[{}]({})", a.filename, a.url),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Some(image) = find_first_image(message) {
|
||||||
|
embed = embed.image(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn from_message(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
||||||
|
static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
Regex::new(r"(?:https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<server_id>\d+)\/(?<channel_id>\d+)\/(?<message_id>\d+)").unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let Some(guild_id) = msg.guild_id else {
|
||||||
|
debug!("Not resolving message in DM");
|
||||||
|
return Ok(Vec::new());
|
||||||
|
};
|
||||||
|
let author = guild_id.member(ctx, msg.author.id).await?;
|
||||||
|
|
||||||
let matches = MESSAGE_PATTERN
|
let matches = MESSAGE_PATTERN
|
||||||
.captures_iter(&msg.content)
|
.captures_iter(&msg.content)
|
||||||
.map(|capture| capture.extract());
|
.map(|capture| capture.extract());
|
||||||
|
|
||||||
let mut embeds: Vec<CreateEmbed> = vec![];
|
let mut embeds: Vec<CreateEmbed> = vec![];
|
||||||
|
|
||||||
for (url, [_server_id, channel_id, message_id]) in matches {
|
for (url, [target_guild_id, target_channel_id, target_message_id]) in matches {
|
||||||
trace!("Attempting to resolve message {message_id} from URL {url}");
|
if target_guild_id != guild_id.to_string() {
|
||||||
|
debug!("Not resolving message from other server");
|
||||||
let channel = ChannelId::from_str(channel_id)
|
continue;
|
||||||
.wrap_err_with(|| format!("Couldn't parse channel ID {channel_id}!"))?
|
|
||||||
.to_channel_cached(ctx.as_ref())
|
|
||||||
.ok_or_else(|| eyre!("Couldn't find Guild Channel from {channel_id}!"))?
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
let author_can_view = if channel.kind == ChannelType::PublicThread
|
|
||||||
|| channel.kind == ChannelType::PrivateThread
|
|
||||||
{
|
|
||||||
let thread_members = channel
|
|
||||||
.id
|
|
||||||
.get_thread_members(ctx)
|
|
||||||
.await
|
|
||||||
.wrap_err("Couldn't get members from thread!")?;
|
|
||||||
|
|
||||||
thread_members
|
|
||||||
.iter()
|
|
||||||
.any(|member| member.user_id == msg.author.id)
|
|
||||||
} else {
|
|
||||||
channel
|
|
||||||
.members(ctx)
|
|
||||||
.wrap_err_with(|| format!("Couldn't get members for channel {channel_id}!"))?
|
|
||||||
.iter()
|
|
||||||
.any(|member| member.user.id == msg.author.id)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !author_can_view {
|
|
||||||
debug!("Not resolving message for author who can't see it");
|
|
||||||
}
|
}
|
||||||
|
trace!("Attempting to resolve message {target_message_id} from URL {url}");
|
||||||
|
|
||||||
let original_message = channel
|
let target_channel = ChannelId::from_str(target_channel_id)?
|
||||||
.message(
|
.to_channel(ctx)
|
||||||
ctx,
|
.await?
|
||||||
MessageId::from_str(message_id)
|
.guild()
|
||||||
.wrap_err_with(|| format!("Couldn't parse message ID {message_id}!"))?,
|
.ok_or_else(|| {
|
||||||
)
|
eyre!("Couldn't find GuildChannel from ChannelId {target_channel_id}!")
|
||||||
.await
|
|
||||||
.wrap_err_with(|| {
|
|
||||||
format!("Couldn't get message from ID {message_id} in channel {channel_id}!")
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let author = CreateEmbedAuthor::new(original_message.author.tag())
|
if !member_can_view_channel(ctx, &author, &target_channel).await? {
|
||||||
.icon_url(original_message.author.default_avatar_url());
|
debug!("Not resolving message for author who can't see it");
|
||||||
let footer = CreateEmbedFooter::new(format!("#{}", channel.name));
|
continue;
|
||||||
|
|
||||||
let mut embed = CreateEmbed::new()
|
|
||||||
.author(author)
|
|
||||||
.color(Colour::BLITZ_BLUE)
|
|
||||||
.timestamp(original_message.timestamp)
|
|
||||||
.footer(footer)
|
|
||||||
.description(format!(
|
|
||||||
"{}\n\n[Jump to original message]({})",
|
|
||||||
original_message.content,
|
|
||||||
original_message.link()
|
|
||||||
));
|
|
||||||
|
|
||||||
if !original_message.attachments.is_empty() {
|
|
||||||
embed = embed.fields(original_message.attachments.iter().map(|a| {
|
|
||||||
(
|
|
||||||
"Attachments".to_string(),
|
|
||||||
format!("[{}]({})", a.filename, a.url),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
if let Some(image) = find_first_image(msg) {
|
|
||||||
embed = embed.image(image);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let target_message_id = MessageId::from_str(target_message_id)?;
|
||||||
|
let target_message = target_channel
|
||||||
|
.message(ctx, target_message_id)
|
||||||
|
.await
|
||||||
|
.wrap_err_with(|| {
|
||||||
|
eyre!("Couldn't find channel message from ID {target_message_id}!")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let embed = to_embed(ctx, &target_message).await?;
|
||||||
|
|
||||||
embeds.push(embed);
|
embeds.push(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue