forked from natto1784/singh3
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
60adc1e937 | |||
af80a94cfb | |||
cdf5c2bce6 | |||
25735e3581 | |||
a3919853b0 | |||
a4e7c64193 | |||
0453caee43 | |||
5779f54f2d | |||
1d63fc050c | |||
05dc40863e | |||
c04ec75f3e | |||
5d24893af6 | |||
cee2e47b6c | |||
cffeff4e27 | |||
d20326a846 | |||
a7a15dc3b1 | |||
18b19f0695 | |||
f59a2c1b7f | |||
9a01af14f7 | |||
8e732b34ca | |||
5c911042be | |||
8177787376 | |||
44aba55f5e | |||
b43472227a | |||
5f8ceb94b2 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Cargo.nix linguist-generated
|
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: main
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
packages:
|
||||
name: test packages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: cachix/install-nix-action@v13
|
||||
with:
|
||||
install_url: https://github.com/numtide/nix-flakes-installer/releases/download/nix-2.4pre20210429_d15a196/install
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: natto1784
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- run: nix build
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,7 @@
|
||||
target/
|
||||
result
|
||||
\#*\#
|
||||
.\#*
|
||||
.*~*~
|
||||
*~
|
||||
result-bin
|
||||
|
714
Cargo.lock
generated
714
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,22 @@
|
||||
cargo-features = ["edition2021"]
|
||||
|
||||
[package]
|
||||
name = "singh3"
|
||||
version = "0.1.0"
|
||||
authors = [ "Amneesh Singh <natto@weirdnatto.in>" ]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tracing = "*"
|
||||
regex = "*"
|
||||
regex = "1"
|
||||
tokio-postgres = "*"
|
||||
rand = "*"
|
||||
|
||||
[dependencies.serenity]
|
||||
version = "0.10.*"
|
||||
version = "0.10.10"
|
||||
features = ["cache", "framework", "standard_framework", "rustls_backend", "unstable_discord_api", "collector"]
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.0"
|
||||
features = ["macros", "signal", "rt-multi-thread"]
|
||||
|
||||
|
10
README.md
Normal file
10
README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
[](https://ci.weirdnatto.in/teams/main/pipelines/singh3) [](https://hub.docker.com/repository/docker/natto17/singh3)
|
||||
# Singh3
|
||||
My stupid discord bot that I will probably stop maintaining again like I did for the past 8+ months
|
||||
Real shit code
|
||||
- Not really a useful bot, run at your own risk
|
||||
- You need postgresql too
|
||||
- Environment Variables
|
||||
- DISCORD_TOKEN={{.your-discord-token}}
|
||||
- DB_URL={{.your-postgresql-db-url}}
|
||||
- Image size is kinda large cuz it uses ubuntu for libraries that are dynamically needed
|
85
ci/pipeline.yml
Normal file
85
ci/pipeline.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
resource_types:
|
||||
- name: nomad
|
||||
type: registry-image
|
||||
source:
|
||||
repository: natto17/concourse-nomad-resource
|
||||
tag: latest
|
||||
|
||||
resources:
|
||||
- name: image
|
||||
type: registry-image
|
||||
icon: docker
|
||||
source:
|
||||
repository: ((docker.user))/singh3
|
||||
tag: latest
|
||||
username: ((docker.user))
|
||||
password: ((docker.pass))
|
||||
|
||||
- name: nomad-job
|
||||
type: nomad
|
||||
source:
|
||||
url: https://nomad.weirdnatto.in
|
||||
name: singh3
|
||||
token: ((nomad.token))
|
||||
consul_token: ((nomad.consul))
|
||||
vault_token: ((nomad.vault))
|
||||
|
||||
- name: repo
|
||||
type: git
|
||||
icon: discord
|
||||
source:
|
||||
uri: https://git.weirdnatto.in/natto1784/singh3.git
|
||||
branch: master
|
||||
|
||||
- name: nix
|
||||
type: registry-image
|
||||
icon: docker
|
||||
source:
|
||||
repository: nixos/nix
|
||||
tag: latest
|
||||
|
||||
jobs:
|
||||
- name: configure-self
|
||||
public: true
|
||||
plan:
|
||||
- get: repo
|
||||
trigger: true
|
||||
- set_pipeline: self
|
||||
file: repo/ci/pipeline.yml
|
||||
- name: singh3
|
||||
plan:
|
||||
- get: repo
|
||||
trigger: true
|
||||
passed: [configure-self]
|
||||
- get: nix
|
||||
trigger: false
|
||||
- task: build
|
||||
image: nix
|
||||
config:
|
||||
params:
|
||||
CACHIX_NAME: ((cachix.name))
|
||||
CACHIX_AUTH_TOKEN: ((cachix.token))
|
||||
inputs:
|
||||
- name: repo
|
||||
outputs:
|
||||
- name: upload
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
nix-env -iA nixpkgs.cachix nixpkgs.gzip
|
||||
cachix use $CACHIX_NAME
|
||||
cachix watch-exec pain nix -- --extra-experimental-features "nix-command flakes" build ./repo
|
||||
nix --extra-experimental-features "nix-command flakes" build ./repo#image -o result
|
||||
gzip -cd < $(readlink result) > ./upload/image.tar
|
||||
- put: image
|
||||
inputs: [upload]
|
||||
params:
|
||||
image: upload/image.tar
|
||||
- put: nomad-job
|
||||
params:
|
||||
job_path: repo/singh3.nomad
|
||||
templating: false
|
||||
restart: true
|
10
default.nix
Normal file
10
default.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
(import
|
||||
(
|
||||
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||
}
|
||||
)
|
||||
{ src = ./.; }
|
||||
).defaultNix
|
108
flake.lock
generated
108
flake.lock
generated
@@ -1,12 +1,47 @@
|
||||
{
|
||||
"nodes": {
|
||||
"cargo2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1648397583,
|
||||
"narHash": "sha256-g1Ej9APkfHjGd9ExBDvpmyAvsKcfMOsNH6p95xc8E/Y=",
|
||||
"owner": "cargo2nix",
|
||||
"repo": "cargo2nix",
|
||||
"rev": "4362ae00fe824d120e94dd5d6f6e63969dc3d264",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cargo2nix",
|
||||
"repo": "cargo2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1614513358,
|
||||
"narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=",
|
||||
"lastModified": 1638122382,
|
||||
"narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5466c5bbece17adaab2d82fae80b46e807611bf3",
|
||||
"rev": "74f7e4319258e287b0f9cb95426c9853b282730b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1637014545,
|
||||
"narHash": "sha256-26IZAc5yzlD9FlDT54io1oqG/bBoyka+FJk5guaX4x4=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "bba5dcc8e0b20ab664967ad83d24d64cb64ec4f4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -16,6 +51,22 @@
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1638109994,
|
||||
"narHash": "sha256-OpA37PTiPMIqoRJbufbl5rOLII7HeeGcA0yl7FoyCIE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a284564b7f75ac4db73607db02076e8da9d42c9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "release-21.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1622966049,
|
||||
"narHash": "sha256-6g+28v94ISkVk9TBSsITVOnB2slK8plieWPIF2jo/l0=",
|
||||
@@ -31,39 +82,66 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1617325113,
|
||||
"narHash": "sha256-GksR0nvGxfZ79T91UUtWjjccxazv6Yh/MvEJ82v1Xmw=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1637453606,
|
||||
"narHash": "sha256-Gy6cwUswft9xqsjWxFYEnx/63/qzaFUwatcbV5GF/GQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "54c1e44240d8a527a8f4892608c4bce5440c3ecb",
|
||||
"rev": "8afc4e543663ca0a6a4f496262cd05233737e732",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"cargo2nix": "cargo2nix",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"rust-overlay": "rust-overlay_2",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"flake-utils": [
|
||||
"cargo2nix",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"cargo2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1623034161,
|
||||
"narHash": "sha256-cbw9X+nVFcpIuBga0hkbtzXbW2fyDWBon6oUN/uQmu0=",
|
||||
"lastModified": 1638152159,
|
||||
"narHash": "sha256-Q0UHsm36cCxk16I/bF1rHJHxjIflESKk2ej76P39j90=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "0b952cdfa37f8b0fc70fc75fbd4605227cd0b272",
|
||||
"rev": "d9a664513558376595e838b21348cdac0ba3115e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1648953213,
|
||||
"narHash": "sha256-kXWcXFwqcvooHjcDoEK4mtvKU/8LuetwhrofU+9KMS0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "caa1a7ea867138d4f4e05bd2f41d707df0a61cde",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
41
flake.nix
41
flake.nix
@@ -1,37 +1,54 @@
|
||||
{
|
||||
description = "A simple filehost written in rust";
|
||||
description = "singh3 discord bot";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = github:nixos/nixpkgs/nixos-unstable;
|
||||
utils.url = github:numtide/flake-utils;
|
||||
rust-overlay.url = github:oxalica/rust-overlay;
|
||||
cargo2nix.url = github:cargo2nix/cargo2nix;
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, rust-overlay }:
|
||||
outputs = { self, nixpkgs, utils, rust-overlay, cargo2nix }:
|
||||
utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let
|
||||
overlays = [ (import rust-overlay) ];
|
||||
overlays =
|
||||
[
|
||||
(import "${cargo2nix}/overlay")
|
||||
rust-overlay.overlay
|
||||
];
|
||||
|
||||
pkgs = import nixpkgs {
|
||||
inherit system overlays;
|
||||
};
|
||||
|
||||
rustPkgs = pkgs.rustBuilder.makePackageSet' {
|
||||
rustChannel = "latest";
|
||||
packageFun = import ./Cargo.nix;
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
rec {
|
||||
devShell = with pkgs; mkShell {
|
||||
buildInputs = [
|
||||
rust-bin.nightly.latest.default
|
||||
rust-analyzer
|
||||
postgresql
|
||||
];
|
||||
};
|
||||
defaultPackage = pkgs.rustPlatform.buildRustPackage rec {
|
||||
pname = "singh3";
|
||||
version = "0.1.0";
|
||||
src = ./. ;
|
||||
nativeBuildInputs = with pkgs; [
|
||||
rust-bin.nightly.latest.default
|
||||
];
|
||||
cargoSha256 = "sha256-K+WHOEo6reNfcs7pOZZmHZfZl4pUqlykfTdqgSyVURU=";
|
||||
|
||||
packages = {
|
||||
default = (rustPkgs.workspace.singh3 { }).bin;
|
||||
image = pkgs.dockerTools.buildImage {
|
||||
name = "singh3";
|
||||
tag = "latest";
|
||||
created = "now";
|
||||
contents = [ packages.default ];
|
||||
config.Cmd = [ "/bin/singh3" ];
|
||||
};
|
||||
};
|
||||
|
||||
defaultPackage = packages.default;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
8
init.sql
8
init.sql
@@ -3,3 +3,11 @@ CREATE TABLE IF NOT EXISTS words (
|
||||
name varchar not null,
|
||||
reg varchar not null,
|
||||
owner varchar );
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id serial primary key,
|
||||
name varchar unique not null,
|
||||
value varchar not null,
|
||||
owner varchar );
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
|
||||
|
24
singh3.nomad
24
singh3.nomad
@@ -2,51 +2,70 @@ job "singh3" {
|
||||
region = "global"
|
||||
datacenters = ["nazrin"]
|
||||
type = "service"
|
||||
|
||||
group "svc" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
|
||||
port "db" {
|
||||
static = 5454
|
||||
to = 5432
|
||||
}
|
||||
}
|
||||
|
||||
vault {
|
||||
policies = ["singh3-policy"]
|
||||
}
|
||||
|
||||
service {
|
||||
name = "singh3-db"
|
||||
port = "db"
|
||||
}
|
||||
|
||||
task "db" {
|
||||
template {
|
||||
data = <<EOF
|
||||
{{with secret "kv/data/singh3/db"}}{{.Data.data.pass}}{{end}}
|
||||
EOF
|
||||
|
||||
destination = "${NOMAD_SECRETS_DIR}/db.pass"
|
||||
}
|
||||
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "postgres:alpine"
|
||||
ports = ["db"]
|
||||
volumes = ["/var/lib/nomad-st/postgres-singh3:/var/lib/postgresql/data"]
|
||||
}
|
||||
|
||||
env {
|
||||
POSTGRES_USER = "singh3"
|
||||
POSTGRES_PASSWORD_FILE = "${NOMAD_SECRETS_DIR}/db.pass"
|
||||
POSTGRES_DB = "singh3"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 256
|
||||
memory = 128
|
||||
cpu = 128
|
||||
memory = 100
|
||||
}
|
||||
}
|
||||
|
||||
task "bot" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "natto17/singh3:latest"
|
||||
force_pull = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 128
|
||||
memory = 100
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOF
|
||||
{{with secret "kv/data/singh3/db"}}
|
||||
@@ -57,6 +76,7 @@ DISCORD_TOKEN="{{.Data.data.token}}"
|
||||
{{end}}
|
||||
RUST_BACKTRACE=1
|
||||
EOF
|
||||
|
||||
destination = "${NOMAD_SECRETS_DIR}/data.env"
|
||||
env = true
|
||||
}
|
||||
|
@@ -1,50 +1,65 @@
|
||||
use crate::lib::components::make_terminal_components;
|
||||
use core::time::Duration;
|
||||
use regex::Regex;
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
collector::component_interaction_collector::ComponentInteractionCollectorBuilder,
|
||||
framework::standard::{macros::command, Args, CommandResult},
|
||||
futures::StreamExt,
|
||||
model::{
|
||||
channel::ReactionType,
|
||||
interactions::{ButtonStyle, InteractionData},
|
||||
prelude::*,
|
||||
},
|
||||
model::{interactions::InteractionResponseType, prelude::*},
|
||||
prelude::*,
|
||||
utils::Colour,
|
||||
};
|
||||
use tokio_postgres::Row;
|
||||
|
||||
#[command]
|
||||
pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join(" ");
|
||||
if query == "" {
|
||||
msg.reply(ctx, "bruh kitna kya?").await?;
|
||||
#[aliases("kitna", "c")]
|
||||
pub async fn count(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
if args.len() > 2 || args.len() == 0 {
|
||||
msg.reply(ctx, "Please use `,count <word> <user>`").await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let query = args.single::<String>().unwrap();
|
||||
let user = if args.len() == 2 {
|
||||
let user = args.single::<UserId>();
|
||||
match user {
|
||||
Ok(id) => match id.to_user(&ctx.http).await {
|
||||
Ok(u) => u,
|
||||
Err(_) => {
|
||||
msg.reply(ctx, "No such user").await?;
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
msg.reply(ctx, "No such user").await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg.author.clone()
|
||||
};
|
||||
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
|
||||
let id = msg.author.id.to_string();
|
||||
let id = user.id.to_string();
|
||||
let mut query_helper = db
|
||||
.query(
|
||||
format!("select name from words where '{}' ~ reg", query).as_str(),
|
||||
&[],
|
||||
)
|
||||
.query("SELECT name FROM words WHERE $1 ~ reg", &[&query])
|
||||
.await?;
|
||||
|
||||
if query_helper.is_empty() {
|
||||
query_helper = db
|
||||
.query(
|
||||
format!("select name from words where name='{}'", query).as_str(),
|
||||
&[],
|
||||
)
|
||||
.query("SELECT name FROM words WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
if query_helper.is_empty() {
|
||||
msg.reply(
|
||||
ctx,
|
||||
format!(
|
||||
"No entry for '{}' found. If you want to add it, run ',count add {}&<regex>'",
|
||||
"No entry for '{}' found. If you want to add it, run `,cadd {} <regex>`",
|
||||
query, query
|
||||
),
|
||||
)
|
||||
@@ -59,42 +74,49 @@ pub async fn kitna(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
};
|
||||
for row in query_helper {
|
||||
let name: &str = row.get(0);
|
||||
let query_result: i32 = db
|
||||
.query_one(
|
||||
format!("select count from user{} where name='{}'", id, name).as_str(),
|
||||
&[],
|
||||
let count_query = db
|
||||
.query(
|
||||
format!("SELECT count FROM user{} WHERE name=$1", id).as_str(),
|
||||
&[&name],
|
||||
)
|
||||
.await?
|
||||
.get(0);
|
||||
reply = reply + &format!("\n{} count for you: {}", name, query_result);
|
||||
.await?;
|
||||
let query_result: i32 = if count_query.is_empty() {
|
||||
0
|
||||
} else {
|
||||
count_query[0].get(0)
|
||||
};
|
||||
reply += &format!("\n{} count for {}: {}", name, user.name, query_result);
|
||||
}
|
||||
msg.reply(ctx, reply).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
pub async fn cadd(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join(" ");
|
||||
let queries = query.splitn(2, "&").collect::<Vec<&str>>();
|
||||
let queries = query.splitn(2, " ").collect::<Vec<&str>>();
|
||||
if queries.len() != 2 {
|
||||
msg.reply(ctx, "Please use the proper syntax: `,count add <name>&<regex>`\nIf you don't know what regex is, just do: `,count add <name>&<name>`")
|
||||
msg.reply(ctx, "Please use the proper syntax: `,cadd <name> <regex>`\nIf you don't know what regex is, just do: `,cadd <name> <name>`")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
if queries[1].contains(" ") {
|
||||
msg.reply(ctx, "Not a valid regex").await?;
|
||||
let r = Regex::new(&format!("(?i){}", queries[1]));
|
||||
|
||||
if r.is_err() {
|
||||
msg.reply(ctx, "Please enter a valid regex").await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let reg = r.unwrap();
|
||||
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
|
||||
let check_existense = db
|
||||
.query(
|
||||
format!("select name, reg from words where name='{}'", queries[0]).as_str(),
|
||||
&[],
|
||||
)
|
||||
.query("SELECT name, reg FROM words WHERE name=$1", &[&queries[0]])
|
||||
.await?;
|
||||
if check_existense.len() != 0 {
|
||||
let reg: String = check_existense[0].get(1);
|
||||
@@ -106,14 +128,8 @@ pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
return Ok(());
|
||||
}
|
||||
db.execute(
|
||||
format!(
|
||||
"insert into words(name, reg, owner) values('{}','(?i){}', '{}')",
|
||||
queries[0],
|
||||
queries[1],
|
||||
msg.author.id.to_string()
|
||||
)
|
||||
.as_str(),
|
||||
&[],
|
||||
"INSERT INTO words(name, reg, owner) VALUES($1, $2, $3)",
|
||||
&[&queries[0], ®.to_string(), &msg.author.id.to_string()],
|
||||
)
|
||||
.await?;
|
||||
msg.reply(ctx, "Added").await?;
|
||||
@@ -121,7 +137,8 @@ pub async fn add(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
#[aliases("crm")]
|
||||
pub async fn cremove(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join(" ");
|
||||
if query == "" {
|
||||
msg.reply(ctx, "remove what?").await?;
|
||||
@@ -133,10 +150,7 @@ pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let owner = db
|
||||
.query(
|
||||
format!("select owner from words where name = '{}'", query).as_str(),
|
||||
&[],
|
||||
)
|
||||
.query("SELECT owner FROM words WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
if owner.len() == 1 {
|
||||
let owner_id: String = owner[0].get(0);
|
||||
@@ -145,24 +159,18 @@ pub async fn rm(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
db.execute(
|
||||
format!("delete from words where name='{}'", query,).as_str(),
|
||||
&[],
|
||||
)
|
||||
db.execute("DELETE FROM words WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
msg.reply(ctx, "Deleted if it existed").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
pub async fn cedit(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join(" ");
|
||||
let queries = query.splitn(2, "&").collect::<Vec<&str>>();
|
||||
if queries.len() != 2 {
|
||||
msg.reply(
|
||||
ctx,
|
||||
"Please use the proper syntax\n,count change <name>&<regex>",
|
||||
)
|
||||
msg.reply(ctx, "Please use the proper syntax\n,cedit <name>&<regex>")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
@@ -176,10 +184,7 @@ pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let owner = db
|
||||
.query(
|
||||
format!("select owner from words where name = '{}'", queries[0]).as_str(),
|
||||
&[],
|
||||
)
|
||||
.query("SELECT owner FROM words WHERE name=$1", &[&queries[0]])
|
||||
.await?;
|
||||
if owner.len() == 1 {
|
||||
let owner_id: String = owner[0].get(0);
|
||||
@@ -189,106 +194,93 @@ pub async fn change(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
}
|
||||
}
|
||||
db.execute(
|
||||
format!(
|
||||
"update words set reg='(?i){}' where name='{}'",
|
||||
queries[1], queries[0]
|
||||
)
|
||||
.as_str(),
|
||||
&[],
|
||||
"UPDATE words SET reg=$1 WHERE name=$2",
|
||||
&[&("(?i)".to_string() + queries[1]), &queries[0]],
|
||||
)
|
||||
.await?;
|
||||
msg.reply(ctx, "Changed the value if it existed").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! make_embed {
|
||||
($e: expr, $cur: expr, $group: expr) => {{
|
||||
$e = $e
|
||||
.title(format!("List of words: Page {}", $cur))
|
||||
fn make_list_embed(cur: usize, group: &[Row]) -> CreateEmbed {
|
||||
let mut e = CreateEmbed::default();
|
||||
e.title(format!("List of words: Page {}", cur))
|
||||
.color(Colour::TEAL);
|
||||
for row in $group {
|
||||
let idx: i32 = row.get(0);
|
||||
for row in group {
|
||||
let idx: i64 = row.get(0);
|
||||
let name: String = row.get(1);
|
||||
let owner_id: String = row.get(3);
|
||||
$e = $e.field(
|
||||
let owner_id: String = row.get(2);
|
||||
e.field(
|
||||
format!("{}. {}", idx, name),
|
||||
format!(" by <@{}>", owner_id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
$e
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! make_terminal_components {
|
||||
($c: expr, $terminal: expr ) => {{
|
||||
$c.create_action_row(|ar| {
|
||||
ar.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("Prev")
|
||||
.emoji(ReactionType::Unicode("\u{2B05}".to_string()))
|
||||
.custom_id("prev")
|
||||
.disabled($terminal == "first")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("Next")
|
||||
.emoji(ReactionType::Unicode("\u{27A1}".to_string()))
|
||||
.custom_id("next")
|
||||
.disabled($terminal == "last")
|
||||
})
|
||||
})
|
||||
}};
|
||||
e
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
||||
#[aliases("cls")]
|
||||
pub async fn clist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let size = if args.len() > 0 {
|
||||
args.single::<usize>()?
|
||||
} else {
|
||||
5usize
|
||||
};
|
||||
|
||||
if size > 15 {
|
||||
msg.reply(ctx, "Please input a number less than 15").await?;
|
||||
()
|
||||
}
|
||||
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let rows = db.query("select * from words", &[]).await?;
|
||||
|
||||
let rows = db
|
||||
.query(
|
||||
"SELECT ROW_NUMBER() OVER (ORDER BY id), name, owner FROM words",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
if rows.is_empty() {
|
||||
msg.reply(ctx, "No words stored").await?;
|
||||
return Ok(());
|
||||
}
|
||||
let groups: Vec<&[Row]> = rows.chunks(5).collect();
|
||||
let mut cur = 1;
|
||||
|
||||
let groups: Vec<&[Row]> = rows.chunks(size).collect();
|
||||
let mut cur = 1;
|
||||
let message = msg
|
||||
.channel_id
|
||||
.send_message(ctx, |m| {
|
||||
m.embed(|mut e| make_embed!(e, cur, groups[cur - 1]))
|
||||
.components(|c| make_terminal_components!(c, "first"))
|
||||
m.set_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("first", groups.len()))
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut collector = ComponentInteractionCollectorBuilder::new(&ctx)
|
||||
.timeout(Duration::from_secs(90))
|
||||
.author_id(msg.author.id)
|
||||
.message_id(message.id)
|
||||
.await;
|
||||
|
||||
while let Some(interaction) = collector.next().await {
|
||||
if let InteractionData::MessageComponent(component) = interaction.data.as_ref().unwrap() {
|
||||
match component.custom_id.as_ref() {
|
||||
match interaction.data.custom_id.as_ref() {
|
||||
"next" => {
|
||||
if cur != groups.len() {
|
||||
cur += 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
cur += 1;
|
||||
m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1]))
|
||||
.components(|c| {
|
||||
make_terminal_components!(
|
||||
c,
|
||||
if cur == groups.len() {
|
||||
"last"
|
||||
} else {
|
||||
"mid"
|
||||
}
|
||||
)
|
||||
})
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == groups.len() { "last" } else { "mid" },
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
@@ -296,25 +288,71 @@ pub async fn ls(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
||||
}
|
||||
"prev" => {
|
||||
if cur != 1 {
|
||||
cur -= 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
cur -= 1;
|
||||
m.create_embed(|mut e| make_embed!(e, cur, groups[cur - 1]))
|
||||
.components(|c| {
|
||||
make_terminal_components!(
|
||||
c,
|
||||
if cur == 1 { "first" } else { "mid" }
|
||||
)
|
||||
})
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == 1 { "first" } else { "mid" },
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
"first" => {
|
||||
cur = 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("first", groups.len()))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
"last" => {
|
||||
cur = groups.len();
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("last", groups.len()))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
"delete" => {
|
||||
message.delete(ctx).await?;
|
||||
msg.delete(ctx).await?;
|
||||
}
|
||||
"range" => {
|
||||
cur = interaction.data.values[0].parse().unwrap();
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == 1 {
|
||||
"first"
|
||||
} else if cur == groups.len() {
|
||||
"last"
|
||||
} else {
|
||||
"mid"
|
||||
},
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@@ -1,9 +1,6 @@
|
||||
use serenity::prelude::*;
|
||||
use serenity::framework::standard::{macros::command, CommandResult};
|
||||
use serenity::model::prelude::*;
|
||||
use serenity::framework::standard::{
|
||||
CommandResult,
|
||||
macros::command
|
||||
};
|
||||
use serenity::prelude::*;
|
||||
|
||||
#[command]
|
||||
pub async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
pub mod general;
|
||||
pub mod count;
|
||||
pub mod general;
|
||||
pub mod minigames;
|
||||
pub mod tags;
|
||||
|
394
src/commands/tags.rs
Normal file
394
src/commands/tags.rs
Normal file
@@ -0,0 +1,394 @@
|
||||
use crate::lib::{components::make_terminal_components, messages::ExtractInfo};
|
||||
use core::time::Duration;
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
collector::component_interaction_collector::ComponentInteractionCollectorBuilder,
|
||||
framework::standard::{macros::command, Args, CommandResult},
|
||||
futures::StreamExt,
|
||||
model::{interactions::InteractionResponseType, prelude::*},
|
||||
prelude::*,
|
||||
utils::Colour,
|
||||
};
|
||||
use tokio_postgres::Row;
|
||||
|
||||
const GUILD_ID: u64 = 874699899067838535;
|
||||
const ROLE_ID: u64 = 957155053184102400;
|
||||
|
||||
#[command]
|
||||
#[aliases("t")]
|
||||
pub async fn tag(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join("");
|
||||
if query == "" {
|
||||
msg.reply(ctx, "Mention the tag retard").await?;
|
||||
return Ok(());
|
||||
}
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
|
||||
let query_helper = db
|
||||
.query("SELECT name, value FROM tags WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
|
||||
if query_helper.is_empty() {
|
||||
const DIST_LIMIT: i32 = 2;
|
||||
let leven = db
|
||||
.query(
|
||||
"SELECT name, levenshtein_less_equal(name, $1, $2) AS lev FROM tags ORDER BY lev LIMIT 1 ",
|
||||
&[&query, &DIST_LIMIT],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let dist: i32 = leven[0].get(1);
|
||||
|
||||
let l = if dist > DIST_LIMIT {
|
||||
"".to_string()
|
||||
} else {
|
||||
let leven_name: String = leven[0].get(0);
|
||||
format!("\nDid you mean `{}`?", leven_name)
|
||||
};
|
||||
|
||||
msg.reply(
|
||||
ctx,
|
||||
format!(
|
||||
"No entry for '{}' found. If you want to add it, run `,tadd {} <value>`{}",
|
||||
query, query, l
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
let value: String = query_helper[0].get(1);
|
||||
msg.reply(ctx, value).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn tadd(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let tag_value = msg.extract_text(2, true);
|
||||
|
||||
if tag_value.is_none() {
|
||||
msg.reply(
|
||||
ctx,
|
||||
"Please use the proper syntax: `,tadd <name> <value>` or attach something",
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tag_name = args.single::<String>().unwrap();
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let check_existense = db
|
||||
.query("SELECT name FROM tags WHERE name=$1", &[&tag_name])
|
||||
.await?;
|
||||
if check_existense.len() != 0 {
|
||||
msg.reply(ctx, format!("This tag already exists")).await?;
|
||||
return Ok(());
|
||||
}
|
||||
db.execute(
|
||||
"INSERT INTO tags(name, value, owner) VALUES($1, $2, $3)",
|
||||
&[&tag_name, &tag_value, &msg.author.id.to_string()],
|
||||
)
|
||||
.await?;
|
||||
msg.reply(ctx, "Added").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[aliases("tcp")]
|
||||
pub async fn tcopy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let queries: Vec<&str> = args.raw().collect::<Vec<&str>>();
|
||||
if queries.len() != 2 {
|
||||
msg.reply(
|
||||
ctx,
|
||||
"Please use the proper syntax: `,tcopy <original> <new>`",
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let check_existense = db
|
||||
.query("SELECT name FROM tags WHERE name=$1", &[&queries[0]])
|
||||
.await?;
|
||||
if check_existense.len() == 0 {
|
||||
msg.reply(ctx, format!("This tag does not exist")).await?;
|
||||
return Ok(());
|
||||
}
|
||||
db.execute(
|
||||
"INSERT INTO tags(name, value, owner) SELECT $1, value, $2 FROM tags WHERE name=$3",
|
||||
&[&queries[1], &msg.author.id.to_string(), &queries[0]],
|
||||
)
|
||||
.await?;
|
||||
msg.reply(ctx, format!("Copied {} to {}", queries[0], queries[1]))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[aliases("trm")]
|
||||
pub async fn tremove(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let query: String = args.raw().collect::<Vec<&str>>().join(" ");
|
||||
if query == "" {
|
||||
msg.reply(ctx, "remove what?").await?;
|
||||
return Ok(());
|
||||
}
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let owner = db
|
||||
.query("SELECT owner FROM tags WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
if owner.len() == 1 {
|
||||
let owner_id: String = owner[0].get(0);
|
||||
if owner_id != msg.author.id.to_string()
|
||||
&& !msg.author.has_role(&ctx.http, GUILD_ID, ROLE_ID).await?
|
||||
{
|
||||
msg.reply(ctx, "You don't even own this tag").await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
db.execute("DELETE FROM tags WHERE name=$1", &[&query])
|
||||
.await?;
|
||||
msg.reply(ctx, format!("Deleted {} if it existed", query))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn tedit(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let tag_value = msg.extract_text(2, true);
|
||||
|
||||
if tag_value.is_none() {
|
||||
msg.reply(
|
||||
ctx,
|
||||
"Please use the proper syntax: `,tadd <name> <value>` or attach something",
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tag_name = args.single::<String>().unwrap();
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let owner = db
|
||||
.query("SELECT owner FROM tags WHERE name=$1", &[&tag_name])
|
||||
.await?;
|
||||
if owner.len() == 1 {
|
||||
let owner_id: String = owner[0].get(0);
|
||||
if owner_id != msg.author.id.to_string()
|
||||
&& !msg.author.has_role(&ctx.http, GUILD_ID, ROLE_ID).await?
|
||||
{
|
||||
msg.reply(ctx, "You don't even own this tag").await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
db.execute(
|
||||
"UPDATE tags SET value=$1 WHERE name=$2",
|
||||
&[&tag_value, &tag_name],
|
||||
)
|
||||
.await?;
|
||||
msg.reply(ctx, "Changed the value if it existed").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_list_embed(cur: usize, group: &[Row]) -> CreateEmbed {
|
||||
let mut e = CreateEmbed::default();
|
||||
e.title(format!("List of tags: Page {}", cur))
|
||||
.color(Colour::FABLED_PINK);
|
||||
for row in group {
|
||||
let idx: i64 = row.get(0);
|
||||
let name: String = row.get(1);
|
||||
let owner_id: String = row.get(2);
|
||||
e.field(
|
||||
format!("{}. {}", idx, name),
|
||||
format!(" by <@{}>", owner_id),
|
||||
false,
|
||||
);
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[aliases("tls")]
|
||||
pub async fn tlist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let size = if args.len() > 0 {
|
||||
args.single::<usize>()?
|
||||
} else {
|
||||
5usize
|
||||
};
|
||||
|
||||
if size > 15 {
|
||||
msg.reply(ctx, "Please input a number less than 15").await?;
|
||||
()
|
||||
}
|
||||
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
|
||||
let rows = db
|
||||
.query(
|
||||
"SELECT ROW_NUMBER() OVER (ORDER BY id), name, owner FROM tags",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
if rows.is_empty() {
|
||||
msg.reply(ctx, "No tags stored").await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let groups: Vec<&[Row]> = rows.chunks(size).collect();
|
||||
let mut cur = 1;
|
||||
let message = msg
|
||||
.channel_id
|
||||
.send_message(ctx, |m| {
|
||||
m.set_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("first", groups.len()))
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut collector = ComponentInteractionCollectorBuilder::new(&ctx)
|
||||
.timeout(Duration::from_secs(90))
|
||||
.author_id(msg.author.id)
|
||||
.message_id(message.id)
|
||||
.await;
|
||||
|
||||
while let Some(interaction) = collector.next().await {
|
||||
match interaction.data.custom_id.as_ref() {
|
||||
"next" => {
|
||||
if cur != groups.len() {
|
||||
cur += 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == groups.len() { "last" } else { "mid" },
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
"prev" => {
|
||||
if cur != 1 {
|
||||
cur -= 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == 1 { "first" } else { "mid" },
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
"first" => {
|
||||
cur = 1;
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("first", groups.len()))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
"last" => {
|
||||
cur = groups.len();
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components("last", groups.len()))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
"delete" => {
|
||||
message.delete(ctx).await?;
|
||||
msg.delete(ctx).await?;
|
||||
}
|
||||
"range" => {
|
||||
cur = interaction.data.values[0].parse().unwrap();
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|m| {
|
||||
m.add_embed(make_list_embed(cur, groups[cur - 1]))
|
||||
.set_components(make_terminal_components(
|
||||
if cur == 1 {
|
||||
"first"
|
||||
} else if cur == groups.len() {
|
||||
"last"
|
||||
} else {
|
||||
"mid"
|
||||
},
|
||||
groups.len(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[aliases(trand)]
|
||||
pub async fn trandom(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||
let data_read = ctx.data.read().await;
|
||||
let db = data_read
|
||||
.get::<crate::Database>()
|
||||
.expect("Expected Database in TypeMap.")
|
||||
.clone();
|
||||
let rand = db
|
||||
.query(
|
||||
"SELECT name, value, owner FROM tags OFFSET floor(random() * (SELECT COUNT(*) FROM tags)) LIMIT 1",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
let name: String = rand[0].get(0);
|
||||
let value: String = rand[0].get(1);
|
||||
let owner: String = rand[0].get(2);
|
||||
let user_id = UserId::from(owner.parse::<u64>().unwrap());
|
||||
msg.reply(
|
||||
ctx,
|
||||
format!(
|
||||
"{}'s tag {}: \n{}",
|
||||
user_id.to_user(&ctx.http).await?.name,
|
||||
name,
|
||||
value
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
@@ -18,46 +18,38 @@ pub async fn count(msg: Message, db: std::sync::Arc<Client>) {
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.expect("cant create a user table");
|
||||
.expect("Can't create a user table");
|
||||
|
||||
for row in db
|
||||
.query("SELECT name, reg FROM words", &[])
|
||||
.await
|
||||
.expect("can't get the words to count")
|
||||
.expect("Can't get the words to count")
|
||||
{
|
||||
let name: &str = row.get(0);
|
||||
let regex: Regex = Regex::new(row.get(1)).unwrap();
|
||||
let count = regex.captures_iter(&msg.content).count();
|
||||
let count: i32 = regex.captures_iter(&msg.content).count() as i32;
|
||||
if count > 0 {
|
||||
let query_result = db
|
||||
.query(
|
||||
format!("SELECT count FROM user{} where name='{}'", id, name).as_str(),
|
||||
&[],
|
||||
format!("SELECT count FROM user{} WHERE name=$1", id).as_str(),
|
||||
&[&name],
|
||||
)
|
||||
.await
|
||||
.expect("cant select the count");
|
||||
.expect("Can't select count");
|
||||
if query_result.is_empty() {
|
||||
db.execute(
|
||||
format!(
|
||||
"insert into user{} (name, count) values ('{}', 0)",
|
||||
id, name
|
||||
)
|
||||
.as_str(),
|
||||
&[],
|
||||
format!("INSERT INTO user{} (name, count) values ($1, 0)", id).as_str(),
|
||||
&[&name],
|
||||
)
|
||||
.await
|
||||
.expect("cant insert shit");
|
||||
.expect("Can't insert count");
|
||||
}
|
||||
db.execute(
|
||||
format!(
|
||||
"UPDATE user{} SET count = count + {} where name='{}'",
|
||||
id, count, name
|
||||
)
|
||||
.as_str(),
|
||||
&[],
|
||||
format!("UPDATE user{} SET count = count + $1 WHERE name=$2", id).as_str(),
|
||||
&[&count, &name],
|
||||
)
|
||||
.await
|
||||
.expect("cant update");
|
||||
.expect("Can't update count");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,15 +2,7 @@ mod count;
|
||||
mod interactions;
|
||||
use serenity::{
|
||||
async_trait,
|
||||
model::{
|
||||
channel::Message,
|
||||
event::ResumedEvent,
|
||||
gateway::Ready,
|
||||
interactions::{
|
||||
ApplicationCommand, Interaction, InteractionData, InteractionResponseType,
|
||||
InteractionType,
|
||||
},
|
||||
},
|
||||
model::{channel::Message, event::ResumedEvent, gateway::Ready},
|
||||
prelude::*,
|
||||
};
|
||||
use tracing::info;
|
||||
@@ -19,12 +11,8 @@ pub struct Handler;
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||
info!("{} connected bhay", ready.user.name);
|
||||
let _ = ApplicationCommand::create_global_application_commands(&ctx.http, |commands| {
|
||||
commands.set_application_commands(interactions::general())
|
||||
})
|
||||
.await;
|
||||
async fn ready(&self, _: Context, ready: Ready) {
|
||||
println!("{} connected bhay", ready.user.name);
|
||||
}
|
||||
async fn resume(&self, _: Context, _: ResumedEvent) {
|
||||
info!("how th when the");
|
||||
@@ -37,23 +25,4 @@ impl EventHandler for Handler {
|
||||
.clone();
|
||||
count::count(msg, db_client).await;
|
||||
}
|
||||
|
||||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||
if interaction.kind == InteractionType::ApplicationCommand {
|
||||
if let Some(InteractionData::ApplicationCommand(data)) = interaction.data.as_ref() {
|
||||
if let Err(why) = interaction
|
||||
.create_interaction_response(&ctx.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| {
|
||||
interactions::responses(data.name.to_string(), message)
|
||||
})
|
||||
})
|
||||
.await
|
||||
{
|
||||
println!("Cannot respond to slash command: {}", why);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/lib/components.rs
Normal file
62
src/lib/components.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use serenity::{
|
||||
builder::{CreateComponents, CreateSelectMenu},
|
||||
model::{channel::ReactionType, interactions::message_component::ButtonStyle},
|
||||
};
|
||||
|
||||
pub fn make_range_select_menu(first: usize, last: usize) -> CreateSelectMenu {
|
||||
let mut sm = CreateSelectMenu::default();
|
||||
sm.custom_id("range")
|
||||
.placeholder("Page No")
|
||||
.options(|mut os| {
|
||||
for x in first..=last {
|
||||
os = os.create_option(|o| o.label(x).value(x));
|
||||
}
|
||||
|
||||
os
|
||||
});
|
||||
sm
|
||||
}
|
||||
|
||||
pub fn make_terminal_components(terminal: &str, pages: usize) -> CreateComponents {
|
||||
let mut c = CreateComponents::default();
|
||||
c.create_action_row(|ar| {
|
||||
ar.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("First")
|
||||
.emoji(ReactionType::Unicode("\u{23EA}".to_string()))
|
||||
.custom_id("first")
|
||||
.disabled(terminal == "first")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("Prev")
|
||||
.emoji(ReactionType::Unicode("\u{25C0}".to_string()))
|
||||
.custom_id("prev")
|
||||
.disabled(terminal == "first")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("Next")
|
||||
.emoji(ReactionType::Unicode("\u{25B6}".to_string()))
|
||||
.custom_id("next")
|
||||
.disabled(terminal == "last")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label("Last")
|
||||
.emoji(ReactionType::Unicode("\u{23E9}".to_string()))
|
||||
.custom_id("last")
|
||||
.disabled(terminal == "last")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(ButtonStyle::Danger)
|
||||
.label("Delete")
|
||||
.emoji(ReactionType::Unicode("\u{1F5D1}".to_string()))
|
||||
.custom_id("delete")
|
||||
})
|
||||
});
|
||||
if pages <= 25 {
|
||||
c.create_action_row(|ar| ar.add_select_menu(make_range_select_menu(1, pages)));
|
||||
}
|
||||
c
|
||||
}
|
40
src/lib/messages.rs
Normal file
40
src/lib/messages.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use serenity::model::channel::Message;
|
||||
|
||||
pub trait ExtractInfo {
|
||||
fn extract_text(&self, skip: usize, with_ref: bool) -> Option<String>;
|
||||
}
|
||||
|
||||
impl ExtractInfo for Message {
|
||||
fn extract_text(&self, skip: usize, with_ref: bool) -> Option<String> {
|
||||
let mut ret: String = String::from("");
|
||||
|
||||
let raw: Vec<&str> = self.content.splitn(skip + 1, " ").collect();
|
||||
if raw.len() == skip + 1 {
|
||||
ret += raw[skip];
|
||||
ret += "\n";
|
||||
} else if raw.len() < skip {
|
||||
return None;
|
||||
}
|
||||
|
||||
ret += &self
|
||||
.attachments
|
||||
.iter()
|
||||
.map(|x| x.url.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
if let Some(msg) = &self.referenced_message {
|
||||
let ref_text = msg.extract_text(0, false);
|
||||
if with_ref && !ref_text.is_none() {
|
||||
ret += "\n";
|
||||
ret += &ref_text.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if ret.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(ret);
|
||||
}
|
||||
}
|
||||
}
|
2
src/lib/mod.rs
Normal file
2
src/lib/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod components;
|
||||
pub mod messages;
|
10
src/main.rs
10
src/main.rs
@@ -1,8 +1,10 @@
|
||||
mod commands;
|
||||
mod handler;
|
||||
mod lib;
|
||||
use commands::count::*;
|
||||
use commands::general::*;
|
||||
use commands::minigames::*;
|
||||
use commands::tags::*;
|
||||
use handler::Handler;
|
||||
use serenity::{
|
||||
client::bridge::gateway::ShardManager,
|
||||
@@ -36,10 +38,13 @@ impl TypeMapKey for Database {
|
||||
struct General;
|
||||
|
||||
#[group]
|
||||
#[prefix = "count"]
|
||||
#[commands(kitna, add, rm, change, ls)]
|
||||
#[commands(count, cadd, cremove, cedit, clist)]
|
||||
struct Count;
|
||||
|
||||
#[group]
|
||||
#[commands(tag, tadd, tcopy, tremove, tedit, tlist, trandom)]
|
||||
pub struct Tags;
|
||||
|
||||
#[group]
|
||||
#[commands(challenge)]
|
||||
struct Minigames;
|
||||
@@ -82,6 +87,7 @@ async fn main() {
|
||||
.help(&MY_HELP)
|
||||
.group(&GENERAL_GROUP)
|
||||
.group(&COUNT_GROUP)
|
||||
.group(&TAGS_GROUP)
|
||||
.group(&MINIGAMES_GROUP);
|
||||
|
||||
let mut client = Client::builder(&token)
|
||||
|
Reference in New Issue
Block a user