added more config options & made python type-safe(r).
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
[general]
|
||||
# The language of the bot. Available languages can be found in the data/lang folder.
|
||||
# NOTE: Do not include the file extension of the language. For example, "en.json" is just "en".
|
||||
lang = "en"
|
||||
|
||||
# The discord bots status text
|
||||
status = "🎲 https://bytedice.net"
|
||||
|
||||
@@ -15,6 +19,12 @@ fetch_limit = 100
|
||||
# Leave empty to search for everything
|
||||
search_flairs = ["Original Art"]
|
||||
|
||||
# Command aliases for the Reddit bot, since those aren't autocomplete-able.
|
||||
# TODO: this
|
||||
[reddit.aliases]
|
||||
add_post = []
|
||||
|
||||
|
||||
[commands]
|
||||
# Disabled selected command categories. All commands and their categories can be viewed in the README.
|
||||
# "admin" and "owner" categories are always enabled.
|
||||
@@ -24,3 +34,6 @@ disabled_categories = [
|
||||
# "help"
|
||||
# "re"
|
||||
]
|
||||
|
||||
# The chance (between 0..1) for the `/8_ball` command to output a "quirky" answer.
|
||||
eight_ball_quirky_chance = 0.2
|
||||
@@ -7,6 +7,7 @@
|
||||
"reason": null
|
||||
},
|
||||
"post_data": {
|
||||
"subreddit": "bytedicetesting",
|
||||
"title": "I JUST BOUGHT THE CONTINENT OF NORTH AMERICA FOR A DOLLAR!",
|
||||
"upvotes": 69420,
|
||||
"date_unix": 1738614657,
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"dc_btn_unremove": "Restore",
|
||||
"dc_btn_unvote": "Un-vote",
|
||||
"dc_btn_vote": "Vote",
|
||||
"dc_msg_8-ball_answer": "## You shook a magic 8-ball\nQ: {0}\nA: {1}",
|
||||
"dc_msg_8-ball_answer": "## You shook a magic 8-ball...\n**Q:** {0}\n**A:** {1}",
|
||||
"dc_msg_add_to_data": "Added your server to my data! Thanks for letting me steal it! (/s)",
|
||||
"dc_msg_bound_channel": "Successfully bound channel ID `{0}` as the \"where all collected Reddit data gets dumped\" channel!",
|
||||
"dc_msg_cmd_404": "No command \"{0}\" found!\nHint: Try `/help` without any arguments or `/help <category>`",
|
||||
@@ -14,7 +14,7 @@
|
||||
"dc_msg_dm_python_err_socket": "Unknown internal Python error occurred: Websocket response error",
|
||||
"dc_msg_dm_python_err": "Unknown internal Python Error:\n```\n{0}\n```",
|
||||
"dc_msg_embed_default_embed_desc": "Default english embed description.",
|
||||
"dc_msg_embed_re_post": "Spoilers and vote length anonymizer for fair review!\n## Post Data:\n**Post upvotes:** ||`{0:>6}`||\n**Moderator votes:** ||`{1:>6}`||\n**Media type:** `{2}`\n**URL:** ||<{3}>||\n\n## Listing Data:\n**Added by:** `{{ human: {4}, bot: {5} }}`\n**Approved by:** `{{ human: {6}, bot: [not implemented] }}`",
|
||||
"dc_msg_embed_re_post": "Spoilers and vote length anonymizer for fair review!\n## Post Data:\n**Subreddit:** {0}\n**Post upvotes:** ||`{1:>6}`||\n**Moderator votes:** ||`{2:>6}`||\n**Media type:** `{3}`\n**URL:** ||<{4}>||\n\n## Listing Data:\n**Added by:** human: {5} | bot: {6}\n**Approved by:** human: {7} | bot: [not implemented]",
|
||||
"dc_msg_embed_re_removed": "## Removed by `{0}`\n**Reason:** {1}\n**URL**: ||<{2}>||",
|
||||
"dc_msg_err_trace": "Unknown error!\nError trace: {0}",
|
||||
"dc_msg_failed_shorturl_conversion": "Couldn't convert to shortURL: Invalid Reddit URL format.",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"dc_btn_unremove": "Restaurer",
|
||||
"dc_btn_unvote": "Annuler le vote",
|
||||
"dc_btn_vote": "Voter",
|
||||
"dc_msg_8-ball_answer": "## Vous avez secoué une boule magique 8\nQ : {0}\nR : {1}",
|
||||
"dc_msg_8-ball_answer": "## Vous avez secoué une boule magique 8...\n**Q :** {0}\n**R :** {1}",
|
||||
"dc_msg_add_to_data": "Serveur ajouté à mes données ! Merci de me laisser les voler ! (/s)",
|
||||
"dc_msg_bound_channel": "Canal avec l’identifiant `{0}` lié avec succès comme canal \"où toutes les données Reddit collectées sont envoyées\" !",
|
||||
"dc_msg_cmd_404": "Commande \"{0}\" introuvable !\nAstuce : Essayez `/help` sans arguments ou `/help <catégorie>`",
|
||||
@@ -14,7 +14,7 @@
|
||||
"dc_msg_dm_python_err_socket": "Erreur Python interne inconnue : erreur de réponse WebSocket",
|
||||
"dc_msg_dm_python_err": "Erreur Python interne inconnue :\n```\n{0}\n```",
|
||||
"dc_msg_embed_default_embed_desc": "Description par défaut de l'encart en anglais.",
|
||||
"dc_msg_embed_re_post": "Spoilers et anonymisation des votes pour une évaluation équitable !\n## Données du post :\n**Votes positifs du post :** ||`{0:>6}`||\n**Votes des modérateurs :** ||`{1:>6}`||\n**Type de média :** `{2}`\n**URL :** ||<{3}>||\n\n## Données de la liste :\n**Ajouté par :** `{{ humain : {4}, bot : {5} }}`\n**Approuvé par :** `{{ humain : {6}, bot : [non implémenté] }}`",
|
||||
"dc_msg_embed_re_post": "Spoilers et anonymiseur de durée des votes pour une évaluation équitable !\n## Données du post :\n**Subreddit :** {0}\n**Votes positifs du post :** ||`{1:>6}`||\n**Votes des modérateurs :** ||`{2:>6}`||\n**Type de média :** `{3}`\n**URL :** ||<{4}>||\n\n## Données d'inscription :\n**Ajouté par :** humain : {5} | bot : {6}\n**Approuvé par :** humain : {7} | bot : [non implémenté]",
|
||||
"dc_msg_embed_re_removed": "## Supprimé par `{0}`\n**Raison :** {1}\n**URL** : ||<{2}>||",
|
||||
"dc_msg_err_trace": "Erreur inconnue !\nTrace de l’erreur : {0}",
|
||||
"dc_msg_failed_shorturl_conversion": "Échec de la conversion en shortURL : format d’URL Reddit invalide.",
|
||||
@@ -55,7 +55,7 @@
|
||||
"dc_msg_update_removing_dupe": "{0}Suppression des posts en double...",
|
||||
"dc_msg_update_removing_old": "{0}Suppression des anciens posts (seuil : {1}j)...",
|
||||
"dc_msg_update_removing": "{0}Suppression des posts supprimés...",
|
||||
"log_lang_load_success": "Fichier de langue anglais chargé avec succès !",
|
||||
"log_lang_load_success": "Fichier de langue français chargé avec succès !",
|
||||
"none": "Aucun",
|
||||
"py_re_response_suffix": "^(Je ne suis pas une IA, juste un bot. Cette action a été effectuée automatiquement. Vous pouvez signaler des bugs et consulter mon code source [ici](https://github.com/ByteDice/ByteDiceAssistant) !)",
|
||||
"py_re_response_weekly_add": "Post ajouté avec succès aux soumissions hebdomadaires d'art ! Merci pour votre participation !",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rand::{seq::IteratorRandom, Rng};
|
||||
|
||||
use crate::{lang, messages::send_msg, Context, Error};
|
||||
use crate::{data::get_toml_mutex, lang, messages::send_msg, Context, Error};
|
||||
|
||||
|
||||
#[poise::command(
|
||||
@@ -16,14 +16,15 @@ pub async fn cmd(
|
||||
#[description = "Question to ask."] question: String
|
||||
) -> Result<(), Error>
|
||||
{
|
||||
let is_quirky = rand::rng().random_bool(0.2);
|
||||
let quirky_chance = get_toml_mutex(&ctx.data().cfg).await.unwrap()["commands"]["eight_ball_quirky_chance"].as_float().unwrap();
|
||||
let is_quirky = rand::rng().random_bool(quirky_chance.clamp(0.0, 1.0));
|
||||
let list = &ctx.data().ball_prompts[if is_quirky { 1 } else { 0 }];
|
||||
let rand_item = list.iter().choose(&mut rand::rng());
|
||||
|
||||
send_msg(
|
||||
ctx,
|
||||
lang!("dc_msg_8-ball_answer", question, rand_item.unwrap()),
|
||||
true,
|
||||
false,
|
||||
true
|
||||
).await;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{data::{get_toml_mutex, read_cfg_data}, lang, messages::send_msg, websocket::send_cmd_json, Context, Error};
|
||||
use crate::{data::{self, get_toml_mutex, read_cfg_data}, lang, messages::send_msg, Context, Error};
|
||||
|
||||
|
||||
#[poise::command(
|
||||
@@ -16,13 +14,15 @@ pub async fn cmd(
|
||||
ctx: Context<'_>
|
||||
) -> Result<(), Error>
|
||||
{
|
||||
read_cfg_data(&ctx.data(), false).await;
|
||||
let r = read_cfg_data(&ctx.data(), false).await;
|
||||
let d = get_toml_mutex(&ctx.data().cfg).await.unwrap();
|
||||
let d_str = toml::to_string(&d)?;
|
||||
let r = send_cmd_json("update_cfg", Some(json!([d_str])), true).await;
|
||||
|
||||
if r.is_none() { return Ok(()); }
|
||||
|
||||
let data_binding = get_toml_mutex(&ctx.data().cfg).await.unwrap();
|
||||
let lang_cfg = data_binding["general"]["lang"].as_str().unwrap();
|
||||
data::load_lang_data(lang_cfg.to_string());
|
||||
|
||||
if r.unwrap()["value"].as_bool().unwrap() {
|
||||
send_msg(
|
||||
ctx,
|
||||
|
||||
+4
-4
@@ -95,7 +95,6 @@ fn generate_re_data() {
|
||||
|
||||
if let Some(bk_week) = preset_json[CFG_DATA_RE].as_object_mut() {
|
||||
bk_week.remove("EXAMPLE VALUE");
|
||||
bk_week.remove("EXAMPLE VALUE DELETED");
|
||||
}
|
||||
|
||||
let json_str = serde_json::to_string_pretty(&preset_json).unwrap();
|
||||
@@ -116,7 +115,7 @@ pub async fn write_re_data() {
|
||||
}
|
||||
|
||||
|
||||
pub async fn read_cfg_data(data: &Data, wipe: bool) {
|
||||
pub async fn read_cfg_data(data: &Data, wipe: bool) -> Option<Value> {
|
||||
if !Path::new(DATA_PATH_CFG).exists() || wipe {
|
||||
rs_println!(
|
||||
"{} creating new from preset...",
|
||||
@@ -128,9 +127,10 @@ pub async fn read_cfg_data(data: &Data, wipe: bool) {
|
||||
let str_data = fs::read_to_string(DATA_PATH_CFG).unwrap();
|
||||
let json_data: toml::Value = str_data.parse().unwrap();
|
||||
let mut cfg_data = data.cfg.lock().await;
|
||||
*cfg_data = Some(json_data);
|
||||
*cfg_data = Some(json_data.clone());
|
||||
|
||||
send_cmd_json("update_cfg", Some(json!([str_data])), true).await;
|
||||
let r = send_cmd_json("update_cfg", Some(json!([toml::to_string(&json_data).unwrap()])), true).await;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-11
@@ -68,12 +68,10 @@ struct Args {
|
||||
wipe: bool,
|
||||
#[arg(short = 't', long, help = "Makes the program use the ASSISTANT_TOKEN_TEST env var instead of ASSISTANT_TOKEN. This env var should hold the token of a non-production bot.")]
|
||||
test: bool,
|
||||
#[arg(long, help = "Removes the annoying ping prints.")]
|
||||
noping: bool,
|
||||
#[arg(long, help = "Adds annoying ping prints.")]
|
||||
ping: bool,
|
||||
#[arg(long, help = "Makes the program not use the schedules.")]
|
||||
nosched: bool,
|
||||
#[arg(long, default_value = "en", help = "Which language file to use (Do not include file extensions)")]
|
||||
lang: String
|
||||
nosched: bool
|
||||
}
|
||||
|
||||
|
||||
@@ -104,12 +102,7 @@ pub static mut NOPING: bool = false;
|
||||
async fn main() {
|
||||
let args = <Args as clap::Parser>::parse();
|
||||
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
|
||||
unsafe { NOPING = args.noping; }
|
||||
|
||||
|
||||
rs_println!("Fetching language file...");
|
||||
data::load_lang_data(args.clone().lang);
|
||||
rs_println!("[IMPORTANT] The below message is a test message, it should be written in the language you've selected\nTest message: {}", lang!("log_lang_load_success"));
|
||||
unsafe { NOPING = !args.ping; }
|
||||
|
||||
let own_env = std::env::var("ASSISTANT_OWNERS").unwrap_or("0".to_string());
|
||||
let own_vec_str: Vec<String> = own_env.split(",").map(String::from).collect();
|
||||
@@ -118,8 +111,15 @@ async fn main() {
|
||||
.map(|s| s.parse::<u64>().expect("Failed to parse ASSISTANT_OWNERS. Invalid syntax."))
|
||||
.collect();
|
||||
|
||||
rs_println!("Generating and/or fetching data and config...");
|
||||
let data = gen_data(args.clone(), own_vec_u64.clone()).await;
|
||||
|
||||
rs_println!("Fetching language file...");
|
||||
let data_binding = get_toml_mutex(&data.cfg).await.unwrap();
|
||||
let lang_cfg = data_binding["general"]["lang"].as_str().unwrap();
|
||||
data::load_lang_data(lang_cfg.to_string());
|
||||
rs_println!("[IMPORTANT] The below message is a test message, it should be written in the language you've selected\nTest message: {}", lang!("log_lang_load_success"));
|
||||
|
||||
if args.test { println!("----- USING TEST BOT -----"); }
|
||||
if args.dev { println!("----- DEV MODE ENABLED -----"); }
|
||||
if args.dev && args.wipe { println!("----- \"DON'T WORRY ABOUT IT\" MODE ENABLED -----"); }
|
||||
|
||||
@@ -219,6 +219,7 @@ pub fn make_post_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOp
|
||||
|
||||
let desc_str = lang!(
|
||||
"dc_msg_embed_re_post",
|
||||
post_data["post_data"]["subreddit"].as_str().unwrap(),
|
||||
post_data["post_data"]["upvotes"].as_i64().unwrap(),
|
||||
post_data["votes"]["mod_voters"].as_array().unwrap().len(),
|
||||
if !media_type.is_null() { media_type.as_str().unwrap() } else { "None" },
|
||||
|
||||
+13
-12
@@ -1,7 +1,7 @@
|
||||
from io import TextIOWrapper
|
||||
import asyncpraw as praw
|
||||
import asyncpraw as praw # type: ignore
|
||||
import os
|
||||
from typing import Final
|
||||
from typing import Final, Any
|
||||
from macros import *
|
||||
import toml
|
||||
|
||||
@@ -11,11 +11,11 @@ CFG_DATA_RE: Final[str] = "reddit"
|
||||
|
||||
|
||||
class Bot:
|
||||
args: dict = {"NO_RUST": True, "dev": True, "py": True, "port": 2920}
|
||||
r_id: str = os.environ.get("ASSISTANT_R_ID")
|
||||
secret: str = os.environ.get("ASSISTANT_R_TOKEN")
|
||||
username: str = os.environ.get("ASSISTANT_R_NAME")
|
||||
password: str = os.environ.get("ASSISTANT_R_PASS")
|
||||
args: dict[str, Any] = {"NO_RUST": True, "dev": True, "py": True, "port": 2920}
|
||||
r_id: str | None = os.environ.get("ASSISTANT_R_ID")
|
||||
secret: str | None = os.environ.get("ASSISTANT_R_TOKEN")
|
||||
username: str | None = os.environ.get("ASSISTANT_R_NAME")
|
||||
password: str | None = os.environ.get("ASSISTANT_R_PASS")
|
||||
|
||||
fetch_limit = 0
|
||||
|
||||
@@ -39,14 +39,14 @@ class Bot:
|
||||
)
|
||||
self.sr_list: list[str] = ["bytedicetesting"]
|
||||
self.sr = None
|
||||
self.data_f: TextIOWrapper = None
|
||||
self.data: dict = {}
|
||||
self.data_f: TextIOWrapper | None = None
|
||||
self.data: dict[str, Any] = {}
|
||||
self.flairs: list[str] = []
|
||||
|
||||
async def initialize(self):
|
||||
self.sr = await self.r.subreddit("+".join(self.sr_list))
|
||||
|
||||
async def set_args(self, args: dict):
|
||||
async def set_args(self, args: dict[str, Any]):
|
||||
self.args = args
|
||||
|
||||
async def stop(self) -> bool:
|
||||
@@ -59,12 +59,13 @@ class Bot:
|
||||
|
||||
async def update_cfg_str(self, new_cfg: str) -> bool:
|
||||
json_cfg = toml.loads(new_cfg)
|
||||
self.update_cfg(json_cfg)
|
||||
await self.update_cfg(json_cfg)
|
||||
return True
|
||||
|
||||
async def update_cfg(self, new_cfg: dict) -> bool:
|
||||
async def update_cfg(self, new_cfg: dict[str, Any]) -> bool:
|
||||
self.sr_list = new_cfg[CFG_DATA_RE]["subreddits"].split("+")
|
||||
self.sr = await self.r.subreddit("+".join(self.sr_list))
|
||||
self.fetch_limit = new_cfg[CFG_DATA_RE]["fetch_limit"]
|
||||
self.flairs = new_cfg[CFG_DATA_RE]["search_flairs"]
|
||||
init_lang(new_cfg["general"]["lang"])
|
||||
return True
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
import json
|
||||
|
||||
from printColors import PrintColors
|
||||
|
||||
|
||||
@@ -8,14 +9,15 @@ G_LANG: dict[str, str] = {}
|
||||
DATA_PATH_LANG: str = "./data/lang/"
|
||||
|
||||
|
||||
def py_print(*args):
|
||||
def py_print(*args: str):
|
||||
print(
|
||||
PrintColors.FG.blue + "Py",
|
||||
"-",
|
||||
" ".join(args) + PrintColors.Special.reset
|
||||
)
|
||||
|
||||
def py_error(*args):
|
||||
|
||||
def py_error(*args: str):
|
||||
print(
|
||||
PrintColors.BG.red + "ERROR" + PrintColors.Special.reset,
|
||||
PrintColors.FG.blue + "Py",
|
||||
@@ -29,13 +31,13 @@ def lang(k: str) -> str:
|
||||
if G_LANG == {}:
|
||||
py_error("Language must be initialized before use!")
|
||||
t = G_LANG.get(k)
|
||||
if k is None: py_error(f"Key not found in language \"{G_LANG_NAME}\": {k}")
|
||||
if t is None: py_error(f"Key not found in language \"{G_LANG_NAME}\": {k}")
|
||||
return str(t)
|
||||
|
||||
|
||||
def init_lang(lang_name: str):
|
||||
global G_LANG, G_LANG_NAME
|
||||
G_LANG_NAME = lang_name
|
||||
G_LANG_NAME = lang_name # type: ignore
|
||||
|
||||
full_path = f"{DATA_PATH_LANG}{lang_name}.json"
|
||||
|
||||
@@ -50,4 +52,4 @@ def init_lang(lang_name: str):
|
||||
except json.JSONDecodeError as e:
|
||||
py_error(f"Failed to parse JSON for language \"{lang_name}\":\n{e}")
|
||||
|
||||
G_LANG = json_data
|
||||
G_LANG = json_data # type: ignore
|
||||
+9
-7
@@ -4,12 +4,12 @@ import time
|
||||
|
||||
from macros import *
|
||||
import bot as botPy
|
||||
import data
|
||||
import py_data
|
||||
import py_websocket
|
||||
|
||||
|
||||
async def main():
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
sys.stdout.reconfigure(encoding="utf-8") # type: ignore
|
||||
|
||||
py_print("Creating Reddit bot...")
|
||||
bot = botPy.Bot()
|
||||
@@ -20,8 +20,10 @@ async def main():
|
||||
# args is supposed to be undefined.
|
||||
# It gets defined in Rust.
|
||||
try:
|
||||
await bot.set_args(args)
|
||||
init_lang(lang_name)
|
||||
await bot.set_args(args) # type: ignore
|
||||
py_print("Fetching language file...")
|
||||
init_lang(lang_name) # type: ignore
|
||||
py_print(f"[IMPORTANT] The below message is a test message, it should be written in the language you've selected\nTest message: {lang('log_lang_load_success')}")
|
||||
except NameError:
|
||||
py_print("No command args or language name found from Rust. Don't worry though, we have backup in place.")
|
||||
init_lang("en")
|
||||
@@ -30,17 +32,17 @@ async def main():
|
||||
py_print("ARGS:", str(bot.args))
|
||||
|
||||
py_print("Reading config file...")
|
||||
await data.read_cfg(bot)
|
||||
await py_data.read_cfg(bot)
|
||||
|
||||
py_print("Reading Reddit data...")
|
||||
rd = data.read_data(bot)
|
||||
rd = py_data.read_data(bot)
|
||||
data_retries = 0
|
||||
|
||||
while not rd :
|
||||
data_retries += 1
|
||||
time.sleep(1)
|
||||
py_print(f"Failed to read data: File doesn't exist yet. Retrying (#{data_retries}/5)...")
|
||||
rd = data.read_data(bot)
|
||||
rd = py_data.read_data(bot)
|
||||
|
||||
if data_retries == 5 and not rd:
|
||||
raise Exception("Couldn't read re_data.json: File doesn't exist")
|
||||
|
||||
+18
-15
@@ -1,10 +1,10 @@
|
||||
import emoji
|
||||
from asyncpraw import models
|
||||
import asyncprawcore as prawcore
|
||||
import asyncpraw.exceptions as exc
|
||||
from asyncpraw import models # type: ignore
|
||||
import asyncprawcore as prawcore # type: ignore
|
||||
import asyncpraw.exceptions as exc # type: ignore
|
||||
import time
|
||||
|
||||
import data
|
||||
import py_data
|
||||
import bot as botPy
|
||||
from macros import *
|
||||
|
||||
@@ -46,7 +46,7 @@ async def add_new_posts(bot: botPy.Bot, max_age: int) -> bool:
|
||||
without_media += 1
|
||||
continue
|
||||
|
||||
post_added = data.add_post_to_data(
|
||||
post_added = py_data.add_post_to_data(
|
||||
bot,
|
||||
details
|
||||
)
|
||||
@@ -60,7 +60,7 @@ async def add_new_posts(bot: botPy.Bot, max_age: int) -> bool:
|
||||
f"{not_added} are removed or already existed, " +
|
||||
f"and {old_posts} were older than the max age threshold.")
|
||||
|
||||
data.write_data(bot)
|
||||
py_data.write_data(bot)
|
||||
|
||||
return True
|
||||
|
||||
@@ -72,6 +72,8 @@ async def fetch_posts_with_flair(bot: botPy.Bot, flair_names: list[str]) -> list
|
||||
f"flair:{flair_names[0]}" if len(flair_names) == 1\
|
||||
else " OR ".join(f"flair:{flair}" for flair in flair_names)
|
||||
|
||||
if bot.sr is None: return []
|
||||
|
||||
# ~36 OG-art posts per week, round limit to 50, 75 or 100
|
||||
async for post in bot.sr.search(f"{flair_names_str}", sort="new", limit=bot.fetch_limit):
|
||||
posts.append(post)
|
||||
@@ -79,8 +81,8 @@ async def fetch_posts_with_flair(bot: botPy.Bot, flair_names: list[str]) -> list
|
||||
return posts
|
||||
|
||||
|
||||
def has_media(post: models.Submission) -> tuple[bool, str, int, list[str]]:
|
||||
media_type: str = None
|
||||
def has_media(post: models.Submission) -> tuple[bool, str | None, int, list[str]]:
|
||||
media_type: str | None = None
|
||||
media_count = 0
|
||||
media_urls: list[str] = []
|
||||
|
||||
@@ -109,7 +111,7 @@ def has_media(post: models.Submission) -> tuple[bool, str, int, list[str]]:
|
||||
return (media_type != None, media_type, media_count, media_urls)
|
||||
|
||||
|
||||
async def from_url(bot: botPy.Bot, url: str) -> tuple[bool, models.Submission]:
|
||||
async def from_url(bot: botPy.Bot, url: str) -> tuple[bool, models.Submission | None]:
|
||||
try:
|
||||
post: models.Submission = await bot.r.submission(url=url)
|
||||
return True, post
|
||||
@@ -120,11 +122,12 @@ async def from_url(bot: botPy.Bot, url: str) -> tuple[bool, models.Submission]:
|
||||
return False, None
|
||||
|
||||
|
||||
def get_post_details(post: models.Submission, added_by_h: bool = False) -> data.PostData:
|
||||
def get_post_details(post: models.Submission, added_by_h: bool = False) -> py_data.PostData:
|
||||
media = has_media(post)
|
||||
|
||||
return data.PostData(
|
||||
return py_data.PostData(
|
||||
post.shortlink,
|
||||
post.subreddit,
|
||||
post.title,
|
||||
post.score,
|
||||
int(post.created_utc),
|
||||
@@ -135,12 +138,12 @@ def get_post_details(post: models.Submission, added_by_h: bool = False) -> data.
|
||||
)
|
||||
|
||||
|
||||
async def add_post_url(bot, url: str, approve: bool = False, added_by_h: bool = False) -> bool:
|
||||
async def add_post_url(bot: botPy.Bot, url: str, approve: bool = False, added_by_h: bool = False) -> bool:
|
||||
result, post = await from_url(bot, url)
|
||||
|
||||
if not result:
|
||||
return False
|
||||
if not result: return False
|
||||
if post is None: return False
|
||||
|
||||
post_data = get_post_details(post, added_by_h)
|
||||
post_data.approved_by_human = approve
|
||||
return data.add_post_to_data(bot, post_data, True)
|
||||
return py_data.add_post_to_data(bot, post_data, True)
|
||||
@@ -1,4 +1,4 @@
|
||||
import asyncpraw.models as models
|
||||
import asyncpraw.models as models # type: ignore
|
||||
|
||||
from macros import *
|
||||
import bot as botPy
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import toml
|
||||
import json
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import bot as botPy
|
||||
from macros import *
|
||||
@@ -17,10 +18,11 @@ class PostData:
|
||||
def __init__(
|
||||
self,
|
||||
url: str,
|
||||
subreddit: str,
|
||||
title: str,
|
||||
upvotes: int,
|
||||
date_unix: int,
|
||||
media_type: str,
|
||||
media_type: str | None,
|
||||
media_urls: list[str],
|
||||
removed: bool = False,
|
||||
removed_by: str | None = None,
|
||||
@@ -33,6 +35,7 @@ class PostData:
|
||||
approved_by_human: bool = False,
|
||||
approved_by_ris: bool = False
|
||||
):
|
||||
self.subreddit = subreddit
|
||||
self.removed = removed
|
||||
self.removed_by = removed_by
|
||||
self.removed_reason = removed_reason
|
||||
@@ -50,7 +53,7 @@ class PostData:
|
||||
self.approved_by_human = approved_by_human
|
||||
self.approved_by_ris = approved_by_ris
|
||||
|
||||
def to_json(self):
|
||||
def to_json(self) -> dict[str, Any]:
|
||||
return {
|
||||
"removed": {
|
||||
"removed": self.removed,
|
||||
@@ -58,6 +61,7 @@ class PostData:
|
||||
"reason": self.removed_reason
|
||||
},
|
||||
"post_data": {
|
||||
"subreddit": self.subreddit,
|
||||
"title": self.title,
|
||||
"upvotes": self.upvotes,
|
||||
"date_unix": self.date_unix,
|
||||
@@ -92,10 +96,9 @@ def read_data(bot: botPy.Bot) -> bool:
|
||||
|
||||
py_print("re_data.json not found, creating new from preset...")
|
||||
with open(os.path.join(DEFAULT_PATH, "re_data_preset.json"), "r") as f:
|
||||
data_preset_json = json.load(f)
|
||||
data_preset_json: dict[str, Any] = json.load(f)
|
||||
|
||||
data_preset_json[botPy.RE_DATA_POSTS].pop("EXAMPLE VALUE", None)
|
||||
data_preset_json[botPy.RE_DATA_POSTS].pop("EXAMPLE VALUE DELETED", None)
|
||||
|
||||
with open(r_path, "w") as f:
|
||||
json.dump(data_preset_json, f, indent = 2)
|
||||
@@ -110,6 +113,8 @@ def read_data(bot: botPy.Bot) -> bool:
|
||||
|
||||
|
||||
def write_data(bot: botPy.Bot) -> bool:
|
||||
if bot.data_f is None: return False
|
||||
|
||||
bot.data_f.seek(0)
|
||||
json.dump(bot.data, bot.data_f, indent=2)
|
||||
bot.data_f.truncate()
|
||||
@@ -128,7 +133,7 @@ async def read_cfg(bot: botPy.Bot) -> bool:
|
||||
data_preset_json = toml.load(f)
|
||||
|
||||
with open(r_path, "w") as f:
|
||||
toml.dump(data_preset_json, f, indent = 2)
|
||||
toml.dump(data_preset_json, f, indent = 2) # type: ignore
|
||||
|
||||
bot.data_f = open(r_path, "r+")
|
||||
|
||||
@@ -224,12 +229,12 @@ def set_vote_post(
|
||||
if remove_vote:
|
||||
if user not in target_voters:
|
||||
return False
|
||||
target_voters.remove(user)
|
||||
target_voters.remove(user) # type: ignore
|
||||
|
||||
else:
|
||||
if user in target_voters:
|
||||
return False
|
||||
target_voters.add(user)
|
||||
target_voters.add(user) # type: ignore
|
||||
|
||||
bot.data[botPy.RE_DATA_POSTS][url]["votes"]["voters_re"] = list(re_voters)
|
||||
bot.data[botPy.RE_DATA_POSTS][url]["votes"]["voters_dc"] = list(dc_voters)
|
||||
+22
-21
@@ -1,13 +1,13 @@
|
||||
import websockets
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from macros import *
|
||||
import bot as botPy
|
||||
import data
|
||||
import py_data
|
||||
import posts
|
||||
import cmds
|
||||
import macros
|
||||
import py_cmds
|
||||
|
||||
ws_global = None
|
||||
is_connected = False
|
||||
@@ -36,9 +36,10 @@ async def websocket_client(bot: botPy.Bot):
|
||||
|
||||
while True:
|
||||
response = await ws.recv()
|
||||
if not response.startswith("json:"):
|
||||
str_response = str(response)
|
||||
if not str_response.startswith("json:"): # type: ignore
|
||||
py_print(f"Received from Rust: {response}")
|
||||
await parse_json(response, bot)
|
||||
await parse_json(str_response, bot)
|
||||
|
||||
|
||||
async def parse_json(response: str, bot: botPy.Bot):
|
||||
@@ -50,7 +51,7 @@ async def parse_json(response: str, bot: botPy.Bot):
|
||||
if json_response["print"]: py_print(f"Received from Rust: {response}")
|
||||
|
||||
result = await json_to_func(json_response, bot)
|
||||
await ws_global.ping()
|
||||
if ws_global is not None: await ws_global.ping()
|
||||
await send_message(f"json:{json.dumps(result)}")
|
||||
except json.JSONDecodeError as e:
|
||||
if bot.args["dev"]: py_print(f"failed to parse json: {json_str}\n reason: {e}")
|
||||
@@ -62,29 +63,29 @@ def run_thread(bot: botPy.Bot):
|
||||
loop.run_until_complete(websocket_client(bot))
|
||||
|
||||
|
||||
async def json_to_func(v: dict, bot: botPy.Bot) -> dict:
|
||||
if "type" not in v or "value" not in v or not isinstance(v, dict):
|
||||
async def json_to_func(v: dict[str, Any], bot: botPy.Bot) -> dict[str, Any]:
|
||||
if "type" not in v or "value" not in v:
|
||||
if bot.args["dev"]: py_print("JSON is not a dictionary or does not include \"type\" and \"value\" keys.")
|
||||
return
|
||||
return result_json(False, True)
|
||||
if v["type"] != "function":
|
||||
v_type = v["type"]
|
||||
if bot.args["dev"]: py_print(f"Type \"{v_type}\" is not supported.")
|
||||
return
|
||||
return result_json(False, True)
|
||||
|
||||
value_supported = True
|
||||
r = False
|
||||
|
||||
match v["value"]:
|
||||
case "update_data_file": r = data .write_data (bot)
|
||||
case "respond_mentions": r = await cmds .respond_to_mention(bot)
|
||||
case "add_new_posts": r = await posts .add_new_posts (bot, *v["args"])
|
||||
case "add_post_url": r = await posts .add_post_url (bot, *v["args"])
|
||||
case "remove_post_url": r = data .remove_post (bot, *v["args"])
|
||||
case "set_approve_post": r = data .set_approve_post (bot, *v["args"])
|
||||
case "set_vote_post": r = data .set_vote_post (bot, *v["args"])
|
||||
case "remove_old_posts": r = data .remove_old_posts (bot, *v["args"])
|
||||
case "update_cfg": r = await bot .update_cfg_str (*v["args"])
|
||||
case "stop_praw": r = await bot .stop ()
|
||||
case "update_data_file": r = py_data.write_data (bot)
|
||||
case "respond_mentions": r = await py_cmds.respond_to_mention(bot)
|
||||
case "add_new_posts": r = await posts .add_new_posts (bot, *v["args"])
|
||||
case "add_post_url": r = await posts .add_post_url (bot, *v["args"])
|
||||
case "remove_post_url": r = py_data.remove_post (bot, *v["args"])
|
||||
case "set_approve_post": r = py_data.set_approve_post (bot, *v["args"])
|
||||
case "set_vote_post": r = py_data.set_vote_post (bot, *v["args"])
|
||||
case "remove_old_posts": r = py_data.remove_old_posts (bot, *v["args"])
|
||||
case "update_cfg": r = await bot .update_cfg_str (*v["args"])
|
||||
case "stop_praw": r = await bot .stop ()
|
||||
case _: value_supported = False
|
||||
|
||||
print_result = v["print"]
|
||||
@@ -97,5 +98,5 @@ async def json_to_func(v: dict, bot: botPy.Bot) -> dict:
|
||||
return result_json(r, print_result)
|
||||
|
||||
|
||||
def result_json(bool: bool, print_result: bool) -> dict:
|
||||
def result_json(bool: bool, print_result: bool) -> dict[str, Any]:
|
||||
return {"type": "result", "value": bool, "print": print_result}
|
||||
+2
-2
@@ -152,12 +152,12 @@ async fn handle_message(msg: tungstenite::protocol::Message, args: Args, owners:
|
||||
}
|
||||
}
|
||||
tungstenite::Message::Binary(bytes) => {
|
||||
if args.dev && !args.noping {
|
||||
if args.dev && args.ping {
|
||||
rs_println!("[Binary] from Python: {:?}", bytes);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if args.dev && !args.noping {
|
||||
if args.dev && args.ping {
|
||||
rs_println!("Received from Python: [UNKNOWN / OTHER]");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user