Compare commits

...

10 commits

Author SHA1 Message Date
433f99929d
Update README.md
Some checks failed
Checking for Secrets / Checking for Secrets (push) Has been cancelled
Checking for Secrets / Job to scan for secrets (push) Has been cancelled
Checking for Secrets / This action scans git repos using awslabs/git-secrets. (push) Has been cancelled
2024-09-17 19:25:37 +10:00
f3811ac712
Update and fix some warnings
Mainly just changing old deprecated functions to their newer counterparts
2024-07-03 17:11:05 +10:00
bbe1ee264b Fix errors from bumping dependencies
It now compiles but you still need to go through all the `//TODO:`s and update the values.
2024-07-03 17:00:04 +10:00
60ac0142e9 Revert poise version 2024-07-03 16:24:16 +10:00
59d752c44c Bump all dep versions
I hope this doesn't break everything...
2024-07-03 16:16:32 +10:00
ab6c2af41e
Automatically check for cargo security issues 2024-07-03 15:47:13 +10:00
18cba1fb39
Add gitlab mirror 2024-07-02 09:01:28 +10:00
Austin
aab98e3e7e
Update README.md
Fixed the fixes
2024-06-29 02:42:13 -07:00
Austin
7e9d15b05e
Update README.md
Fixed spelling errors in the README
2024-06-29 00:31:03 -07:00
d1278ce4e0 Added list of repo mirrors 2024-06-29 14:02:46 +10:00
9 changed files with 966 additions and 668 deletions

14
.github/workflows/cargo-audit.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Security audit
on:
push:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: rustsec/audit-check@v1.4.1
with:
token: ${{ secrets.GITHUB_TOKEN }}

1471
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -24,36 +24,36 @@ poise = { version = "0.5.6", features = [
"cache",
"collector",
] }
tokio = { version = "1.32.0", features = ["full"] }
csv = "1.2.2"
serde = "1.0.188"
serde_json = "1.0.107"
tokio = { version = "1.38.0", features = ["full"] }
csv = "1.3.0"
serde = "1.0.203"
serde_json = "1.0.120"
uwuify = "0.2.2"
chrono = "0.4.31"
clap = { version = "4.0.32", features = ["derive"] }
chrono = "0.4.38"
clap = { version = "4.5.8", features = ["derive"] }
maplit = "1.0.2"
colored = "2.0.4"
colored = "2.1.0"
strip_markdown = "0.2.0"
reqwest = "0.11.20"
futures = "0.3.28"
redis = { version = "0.23.3", features = ["aio", "tokio-comp"] }
reqwest = { version = "0.12.5", features = ["json"] }
futures = "0.3.30"
redis = { version = "0.25.4", features = ["aio", "tokio-comp"] }
merge = { version = "0.1.0", features = ["std", "num", "derive"] }
derivative = "2.2.0"
rand = "0.8.5"
unix-time = "0.1.5"
regex = "1.9.5"
once_cell = "1.18.0"
chrono-tz = "0.8.3"
meilisearch-sdk = "0.17.0"
serde_with = "3.3.0"
tracing = { version = "0.1.37", features = ["async-await"] }
tracing-subscriber = { version = "0.3.17", features = [
regex = "1.10.5"
once_cell = "1.19.0"
chrono-tz = "0.9.0"
meilisearch-sdk = "0.26.1"
serde_with = "3.8.2"
tracing = { version = "0.1.40", features = ["async-await"] }
tracing-subscriber = { version = "0.3.18", features = [
"parking_lot",
"registry",
] }
linkify = "0.10.0"
anyhow = "1.0.75"
thiserror = "1.0.49"
anyhow = "1.0.86"
thiserror = "1.0.61"
pastemyst = "1.0.0"
# oxipng = "5.0.1"
# mozjpeg = "0.9.3"

View file

@ -4,27 +4,27 @@ This is the source code for the [FBTHeaven discord bot](https://fbtsecurity.fbth
[FBT Links](https://linktr.ee/FBT_Heaven)
## about
## About
I am the developer for version 2.0, it has been a stale unmaintained project for months but I didn't want my source to just wither untouched so I removed the API keys and added a hand full of `// TODO:`s to the code for you to find what discord IDs you need to change and what other keys you need to provide.
I am the developer for version 2.0, it has been a stale unmaintained project for months but I didn't want my source to just wither untouched -- so I removed the API keys and added a handful of `// TODO:`s to the code for you to find what discord IDs you need to change and what other keys you need to provide.
You will need a [Redis DB](https://redis.io/) for a bunch of features, no guide on setting one up atm (or possibly every, we'll see how I feel later)
You will need a [Redis DB](https://redis.io/) for a bunch of features, no guide on setting one up atm (or possibly ever, we'll see how I feel later)
You can also update a [Meilisearch DB](https://www.meilisearch.com/) with the same data, that one command is easy to comment out if you don't want to use that too
## redis layout
## Redis layout
The db is split into 8 "folders".
Redis is a key:value DB meaning you have just the name of the DB entry and then it's value, an example of this is the entry `user:0000000000000000000` which is an entry in the user "folder".
Redis is a key:value DB meaning you have just the name of the DB entry and then its value, an example of this is the entry `user:0000000000000000000` which is an entry in the user "folder".
here are the "folders" and their descriptions:
- `authed-server-users:<DiscordServerID>`
- This is a Redis [SET](https://redis.io/docs/latest/develop/data-types/sets/) of discord user IDs who are authenticated in the server in the DB entry
- `cleared-suer:<DiscordUserID>`
- this is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry of users who are cleared as okay in the DB after being flagged
- Json format:
- This is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry of users who are cleared as okay in the DB after being flagged
- JSON format:
```JSON
```json
{
"user_id": "0000000000000000000",
"username": "TestUsername#0001",
@ -36,7 +36,7 @@ here are the "folders" and their descriptions:
- `feedback:<timestamp>-<DiscordUserID>-<DiscordUserName>`
- This is just a [String](https://redis.io/docs/latest/develop/data-types/strings/) containing whatever feedback they put in the feedback command
- `guild-settings:<DiscordGuildID>`
- This is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry containing if the server has auto kick enabled or not as well as the channel id for bot announcements
- This is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry containing if the server has auto kick enabled or not as well as the channel ID for bot announcements
```json
{
@ -46,8 +46,8 @@ here are the "folders" and their descriptions:
}
```
- `monitroted-guild:<DiscordGuildID>`
- This is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry containing info about tracked servers. this is only inside of the `_deprecated.rs` as it was a hold over from the old Python verion's SQLite DB. more info about this one will come with the Python source code later™
- `monitored-guild:<DiscordGuildID>`
- This is a [JSON](https://redis.io/docs/latest/develop/data-types/json/) entry containing info about tracked servers. this is only inside of the `_deprecated.rs` as it was a holdover from the old Python version's SQLite DB. More info about this one will come with the Python source code later™
```json
{
@ -60,7 +60,7 @@ here are the "folders" and their descriptions:
```
- `status:commands-executed`
- this is a simple [String](https://redis.io/docs/latest/develop/data-types/strings/) entry to track how many commands have run since the feature was implemented, it appears at the bottom of the `/about` command
- This is a simple [String](https://redis.io/docs/latest/develop/data-types/strings/) entry to track how many commands have run since the feature was implemented, it appears at the bottom of the `/about` command
- `user:<DiscordUserID>`
- This is the largest list of entries in the DB.
- These are [JSON](https://redis.io/docs/latest/develop/data-types/json/) entries for users who are uploaded via scrapped discords, more info on that in the next section
@ -84,24 +84,31 @@ here are the "folders" and their descriptions:
- `kick-whitelist`
- This is a [SET](https://redis.io/docs/latest/develop/data-types/sets/) that I didn't put in a folder for some reason. It holds a list of DiscordUserIDs that should be ignored when running the `/excel` command.
# scrape discords
# Scrape Discords
So the main use of this bot origionally was as follows:
So the main use of this bot originally was as follows:
- scrape discord using [DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter)
- run resulting .csv files though [my old parser](https://github.com/BuyMyMojo/discord-chat-parser)
- run resulting .csv files through [my old parser](https://github.com/BuyMyMojo/discord-chat-parser)
- resulting file to the bot using the `/excel` command
This was to be used to scrape servers of bad actors such as VRC model rippers and leakers, it would add every user found inside the server into the DB with a description of the server that they are found in.
Once in the DB whenever the user joined a server with the FBT bot active the server owner/mods would get notified of a bad actor joining or optionally it would auto kick these accounts.
Once in the DB whenever the user joined a server with the FBT bot active the server owner/mods would get notified of a bad actor joining or optionally it would auto-kick these accounts.
# that's all folks
# That's all folks
I might clean this up further or add a branch for the old python version in the future.
I might clean this up further or add a branch for the old Python version in the future.
good luck whoever may look upon my first large Rust project
good luck to whoever may look upon my first large Rust project
# Mirrors
- [GitHub](https://github.com/BuyMyMojo/open-fbt)
- [GitLab](https://gitlab.com/BuyMyMojo/open-fbt)
- [Codeberg](https://codeberg.org/BuyMyMojo/open-fbt)
- [Personal Gitea](https://git.buymymojo.net/BuyMyMojo/open-fbt)
<p style="color: rgba(0, 0, 0, 0)">
There is no database files or user entries here, look somewhere else <3
There are no database files or user entries here, look somewhere else <3
</p>

View file

@ -187,6 +187,7 @@ pub async fn search(
ctx: Context<'_>,
#[description = "Member to search for. This must be a user ID."] user_id: String,
) -> Result<(), Error> {
use chrono::DateTime;
use pastemyst::paste::*;
use pastemyst::str;
use poise::serenity_prelude::User;
@ -381,12 +382,11 @@ pub async fn search(
#[allow(clippy::cast_possible_truncation)]
// this shouldn't be able to break but just in case I'm making the `unwrap_or` output NaiveDateTime::MIN
let date_time_stamp = chrono::NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0)
.unwrap_or(chrono::NaiveDateTime::MIN);
let date_time_stamp = DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
let age = chrono::Utc::now()
.naive_utc()
.signed_duration_since(date_time_stamp)
.signed_duration_since(date_time_stamp.naive_local())
.num_days();
let is_user_in_db: Option<String> = check_username_against_db(ctx.author().id.0).await.unwrap();
@ -541,10 +541,10 @@ pub async fn update_search_engine(ctx: Context<'_>) -> Result<(), Error> {
ctx.defer_ephemeral().await?;
// Create a client (without sending any request so that can't fail)
let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY);
let client = Client::new(MEILISEARCH_HOST, Some(MEILISEARCH_API_KEY));
// connect to index "entries"
let entries = client.index("entries");
let entries = client?.index("entries");
let msg = ctx
.send(|b| b.content("Connected to search engine"))
@ -626,6 +626,8 @@ pub async fn footprint_lookup(
BlacklistOutput,
>,
) -> Result<(), Error> {
use chrono::DateTime;
ctx.defer().await?;
let mut con = open_redis_connection().await?;
@ -929,12 +931,11 @@ pub async fn footprint_lookup(
#[allow(clippy::cast_possible_truncation)]
// this shouldn't be able to break but just in case I'm making the `unwrap_or` output NaiveDateTime::MIN
let date_time_stamp = chrono::NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0)
.unwrap_or(chrono::NaiveDateTime::MIN);
let date_time_stamp = DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
let age = chrono::Utc::now()
.naive_utc()
.signed_duration_since(date_time_stamp)
.signed_duration_since(date_time_stamp.naive_local())
.num_days();
let is_user_in_db: Option<String> =

View file

@ -1,4 +1,4 @@
use chrono::NaiveDateTime;
use chrono::DateTime;
use poise::serenity_prelude::{self as serenity, AttachmentType, RichInvite};
use rusted_fbt_lib::{
checks::guild_auth_check,
@ -26,11 +26,11 @@ pub async fn account_age(
#[allow(clippy::cast_possible_truncation)]
// this shouldn't be able to break but just in case I'm making the `unwrap_or` output NaiveDateTime::MIN
let date_time_stamp =
NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0).unwrap_or(NaiveDateTime::MIN);
DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
let age = chrono::Utc::now()
.naive_utc()
.signed_duration_since(date_time_stamp)
.signed_duration_since(date_time_stamp.naive_local())
.num_days();
ctx.say(format!(
@ -56,7 +56,7 @@ pub async fn creation_date(
#[allow(clippy::cast_possible_truncation)]
// this shouldn't be able to break but just in case I'm making the `unwrap_or` output NaiveDateTime::MIN
let date_time_stamp =
NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0).unwrap_or(NaiveDateTime::MIN);
DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
ctx.say(format!("Created/Joined on {date_time_stamp}"))
.await?;
@ -277,11 +277,11 @@ pub async fn invite_info(
#[allow(clippy::cast_possible_truncation)]
// this shouldn't be able to break but just in case I'm making the `unwrap_or` output NaiveDateTime::MIN
let date_time_stamp =
NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0).unwrap_or(NaiveDateTime::MIN);
DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
let age = chrono::Utc::now()
.naive_utc()
.signed_duration_since(date_time_stamp)
.signed_duration_since(date_time_stamp.naive_local())
.num_days();
let is_user_in_db: Option<String> =
@ -293,7 +293,7 @@ pub async fn invite_info(
// TODO: set your own channel ID!
// log user name, id, guild name, id and url to channel
serenity::ChannelId()
serenity::ChannelId(00000000000000000)
.send_message(ctx, |f| {
f.embed(|e| {
e.title("User requested invite info")

View file

@ -1,7 +1,7 @@
use crate::structs::{GuildSettings, UserInfo, WaybackResponse, WaybackStatus};
use crate::utils::snowflake_to_unix;
use crate::vars::FBT_GUILD_ID;
use chrono::NaiveDateTime;
// use chrono::NaiveDateTime;
use chrono::Utc;
use chrono_tz::Australia::Melbourne;
use colored::Colorize;
@ -90,6 +90,8 @@ pub async fn alt_kicker(
ctx: &serenity::Context,
member: &serenity::Member,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
use chrono::DateTime;
use crate::utils::open_redis_connection;
use std::collections::HashSet;
@ -133,12 +135,11 @@ pub async fn alt_kicker(
#[allow(clippy::pedantic)]
// it literally only take's i64, no need to warn about truncation here.
let date_time_stamp = NaiveDateTime::from_timestamp_opt(unix_timecode as i64, 0)
.unwrap_or(NaiveDateTime::MIN);
let date_time_stamp = DateTime::from_timestamp(unix_timecode as i64, 0).unwrap_or(DateTime::UNIX_EPOCH);
let age = chrono::Utc::now()
.naive_utc()
.signed_duration_since(date_time_stamp)
.signed_duration_since(date_time_stamp.naive_local())
.num_days();
// Compare user age

View file

@ -36,7 +36,7 @@ pub async fn pog_be_gone(
let mut hits: Vec<&str> = Vec::new();
for word in words {
POG_RE.find(word).map_or((), |pog| {
let _ = POG_RE.find(word).map_or((), |pog| {
hits.push(pog.as_str());
});
}

View file

@ -25,9 +25,9 @@ pub fn verbose_mode() -> bool {
/// Open a tokio redis connection
#[cfg(feature = "database")]
#[instrument()]
pub async fn open_redis_connection() -> Result<redis::aio::Connection, anyhow::Error> {
pub async fn open_redis_connection() -> Result<redis::aio::MultiplexedConnection, anyhow::Error> {
let redis_connection = redis::Client::open(REDIS_ADDR)?
.get_tokio_connection()
.get_multiplexed_tokio_connection()
.await?;
Ok(redis_connection)
@ -38,7 +38,7 @@ pub async fn open_redis_connection() -> Result<redis::aio::Connection, anyhow::E
#[instrument(skip(con))]
pub async fn set_guild_settings(
ctx: Context<'_>,
con: &mut redis::aio::Connection,
con: &mut redis::aio::MultiplexedConnection,
settings: GuildSettings,
) -> Result<(), Error> {
let json = serde_json::to_string(&settings).unwrap();
@ -64,7 +64,7 @@ pub async fn set_guild_settings(
#[instrument(skip(con))]
pub async fn auth(
ctx: Context<'_>,
con: &mut redis::aio::Connection,
con: &mut redis::aio::MultiplexedConnection,
uid: String,
) -> Result<(), Error> {
redis::cmd("SADD")