diff --git a/README.md b/README.md index d9d88b0..8966b4f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,100 @@ A media streaming bot for TeamTalk. * On Linux run ./TTMediaBot.sh; * On Windows run python TTMediaBot.py directly. +## Scheduler Commands + +TTMediaBot includes a built-in scheduler system (like `cron`) that allows you to schedule bot commands to be executed automatically at specific times. + +### Enable or disable the scheduler + +```bash +cr toggle +``` + +Use this to turn the scheduler **on or off**. It must be enabled before any scheduled tasks can run. + +--- + +### Add a scheduled command + +```bash +cr add | +``` + +This adds a new scheduled task. The command should be split using `|`: + +* The **left side** is the cron time expression. +* The **right side** is the bot command to run. + +#### Examples: + +```bash +cr add */15 * * * *|p something just like this +``` + +Every 15 minutes, the bot will play the song **"Something Just Like This"** using the `p` command. + +```bash +cr add 0 3 * * *|rsrs +``` + +Every day at 3:00 AM, the bot will **restart itself** using the `rsrs` command. + +--- + +### Remove a scheduled command + +```bash +cr rm +``` + +Removes a task from the scheduler. You can find the task number using `cr ls`. + +Example: + +```bash +cr rm 1 +``` + +This removes task number 1. + +--- + +### View all scheduled tasks + +```bash +cr ls +``` + +This command shows all currently scheduled tasks, including: + +* Cron expression +* Command +* Last run time +* Next run time + +--- + +### Cron Expression Guide (Quick) + +### πŸ“ Cron Expression Guide (Quick) + +| Field | Allowed Values | Description | +|---------------|---------------------------|-----------------------| +| Minute | 0–59 | Minute of the hour | +| Hour | 0–23 | Hour of the day | +| Day of month | 1–31 | Day of the month | +| Month | 1–12 | Month of the year | +| Day of week | 0–6 or Sun–Sat | Day of the week | + +**Example:** +- `*/10 * * * *` β†’ Every 10 minutes +- `0 9 * * 1-5` β†’ At 9:00 AM, Monday to Friday +For example: + +* `*/10 * * * *` = every 10 minutes +* `0 9 * * 1-5` = every weekday at 9:00 AM + ### Running in Docker You can also run the bot in a Docker container. First of all, You should build an image from the provided Dockerfile: diff --git a/bot/cache.py b/bot/cache.py index 152403b..3032e23 100644 --- a/bot/cache.py +++ b/bot/cache.py @@ -12,25 +12,37 @@ import portalocker if TYPE_CHECKING: from bot.player.track import Track - cache_data_type = Dict[str, Any] class Cache: def __init__(self, cache_data: cache_data_type): - self.cache_version = cache_data["cache_version"] if "cache_version" in cache_data else CacheManager.version - self.recents: deque[Track] = ( - cache_data["recents"] - if "recents" in cache_data - else deque(maxlen=app_vars.recents_max_lenth) - ) - self.favorites: Dict[str, List[Track]] = ( - cache_data["favorites"] if "favorites" in cache_data else {} + self.cache_version = cache_data.get("cache_version", CacheManager.version) + self.recents: deque[Track] = deque( + cache_data.get("recents", []), + maxlen=app_vars.recents_max_lenth ) + self.favorites: Dict[str, List[Track]] = cache_data.get("favorites", {}) @property def data(self): - return {"cache_version": self.cache_version, "recents": self.recents, "favorites": self.favorites} + # Pastikan semua track di-recents & favorites adalah versi raw (non-stream) + sanitized_recents = deque( + (track.get_raw() if hasattr(track, "get_raw") else track) + for track in self.recents + ) + sanitized_favorites = { + user: [ + track.get_raw() if hasattr(track, "get_raw") else track + for track in tracks + ] + for user, tracks in self.favorites.items() + } + return { + "cache_version": self.cache_version, + "recents": sanitized_recents, + "favorites": sanitized_favorites + } class CacheManager: diff --git a/changelog.txt b/changelog.txt index 4d01a8e..8825a8e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,9 @@ This change log is written to find out the changes that have been made by Pandora, and the source code still refers to TTMediaBot. +5/14/2025 +Added entry for scheduler command on readme.md +Fixed file caching: +1. Favorite list is now saving the original link , instead of internal googlevideo link. +2. Recents now store the original link (e.g. YouTube URL), so the stream will no longer expire. 5/13/2025 Added more information to the client name. useless but why not? diff --git a/dd.py b/dd.py index 30ecfcd..b44893d 100644 --- a/dd.py +++ b/dd.py @@ -1,69 +1,32 @@ -import logging -import sys import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from bot.TeamTalk import TeamTalk, Event, EventType, MessageType - -# Load the watchlist from a file (watchlist.txt) def load_watchlist(): - """Load the watchlist of prohibited words.""" - watchlist = [] try: - with open("watchlist.txt", "r") as f: - watchlist = [line.strip().lower() for line in f.readlines()] + with open("watchlist.txt", "r", encoding="utf-8") as f: + return [line.strip().lower() for line in f if line.strip()] except FileNotFoundError: - logging.error("Watchlist file not found. Please create 'watchlist.txt'.") - return watchlist + return [] - -# Check if the user's message contains any prohibited words -def contains_prohibited_word(message: str, watchlist: list) -> bool: - """Check if a message contains any prohibited words from the watchlist.""" - for word in watchlist: - if word in message.lower(): - return True - return False - - -# Kick the user and broadcast the reason -def kick_user_and_broadcast(ttclient: TeamTalk, user_name: str) -> None: - """Kick the user and broadcast a message about the kick.""" - ttclient.kick_user(user_name) - broadcast_message = f"{user_name} has been kicked for using prohibited language." - ttclient.broadcast_message(broadcast_message) - logging.info(f"Kicked user: {user_name} for prohibited message.") - - -# Event handler for a user text message -def on_user_text_message(event: Event, ttclient: TeamTalk): - """Handle the event when a user sends a text message.""" - watchlist = load_watchlist() - message = event.message.text - user_name = event.message.sender.name - - # Check if the message contains a prohibited word - if contains_prohibited_word(message, watchlist): - logging.info(f"User {user_name} used a prohibited word. Kicking user...") - kick_user_and_broadcast(ttclient, user_name) - else: - logging.info(f"User {user_name}'s message: '{message}' is clean.") - - -# Map event types to their respective handler functions -event_handlers = { - EventType.USER_TEXT_MESSAGE: on_user_text_message, - # You can add more event handlers here for different event types. -} - - -def run_event_handler(event: Event, ttclient: TeamTalk): - """Run the appropriate event handler based on the event type.""" +def on_user_joined(user, bot): try: - event_handler = event_handlers.get(event.event_type) - if event_handler: - event_handler(event, ttclient) - else: - logging.info(f"No handler for event type: {event.event_type.name}") + bot_channel_id = bot.ttclient.get_my_channel_id() + if user.channelid == bot_channel_id: + welcome_msg = f"πŸ‘‹ Hello {user.nickname}, welcome to the channel!" + bot.send_channel_message(welcome_msg) except Exception as e: - logging.error(f"Error in event handling: {e}") + print(f"[ERROR] Failed to send welcome message: {e}") + +def on_user_loggedin(user, bot): + try: + watchlist = load_watchlist() + username = user.nickname.lower() + + for badname in watchlist: + if badname in username: + reason = "🚫 You are kicked because your nickname contains inappropriate words." + bot.send_private_message(user.userid, reason) + bot.kick_user(user.userid, bot.ttclient.get_my_channel_id()) + print(f"[INFO] Kicked user: {user.nickname}") + break + except Exception as e: + print(f"[ERROR] Failed to check user login: {e}")