pushing new things, changelog.txt might help.
Some checks are pending
Build-nightly / docker (push) Waiting to run
Some checks are pending
Build-nightly / docker (push) Waiting to run
This commit is contained in:
parent
edd542f890
commit
f0cedb1dff
94
README.md
94
README.md
@ -22,6 +22,100 @@ A media streaming bot for TeamTalk.
|
|||||||
* On Linux run ./TTMediaBot.sh;
|
* On Linux run ./TTMediaBot.sh;
|
||||||
* On Windows run python TTMediaBot.py directly.
|
* 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 <cron expression>|<command>
|
||||||
|
```
|
||||||
|
|
||||||
|
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 <task number>
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
### Running in Docker
|
||||||
You can also run the bot in a Docker container.
|
You can also run the bot in a Docker container.
|
||||||
First of all, You should build an image from the provided Dockerfile:
|
First of all, You should build an image from the provided Dockerfile:
|
||||||
|
32
bot/cache.py
32
bot/cache.py
@ -12,25 +12,37 @@ import portalocker
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bot.player.track import Track
|
from bot.player.track import Track
|
||||||
|
|
||||||
|
|
||||||
cache_data_type = Dict[str, Any]
|
cache_data_type = Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
class Cache:
|
class Cache:
|
||||||
def __init__(self, cache_data: cache_data_type):
|
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.cache_version = cache_data.get("cache_version", CacheManager.version)
|
||||||
self.recents: deque[Track] = (
|
self.recents: deque[Track] = deque(
|
||||||
cache_data["recents"]
|
cache_data.get("recents", []),
|
||||||
if "recents" in cache_data
|
maxlen=app_vars.recents_max_lenth
|
||||||
else deque(maxlen=app_vars.recents_max_lenth)
|
|
||||||
)
|
|
||||||
self.favorites: Dict[str, List[Track]] = (
|
|
||||||
cache_data["favorites"] if "favorites" in cache_data else {}
|
|
||||||
)
|
)
|
||||||
|
self.favorites: Dict[str, List[Track]] = cache_data.get("favorites", {})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
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:
|
class CacheManager:
|
||||||
|
@ -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.
|
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
|
5/13/2025
|
||||||
Added more information to the client name. useless but why not?
|
Added more information to the client name. useless but why not?
|
||||||
|
|
||||||
|
85
dd.py
85
dd.py
@ -1,69 +1,32 @@
|
|||||||
import logging
|
|
||||||
import sys
|
|
||||||
import os
|
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():
|
def load_watchlist():
|
||||||
"""Load the watchlist of prohibited words."""
|
|
||||||
watchlist = []
|
|
||||||
try:
|
try:
|
||||||
with open("watchlist.txt", "r") as f:
|
with open("watchlist.txt", "r", encoding="utf-8") as f:
|
||||||
watchlist = [line.strip().lower() for line in f.readlines()]
|
return [line.strip().lower() for line in f if line.strip()]
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logging.error("Watchlist file not found. Please create 'watchlist.txt'.")
|
return []
|
||||||
return watchlist
|
|
||||||
|
|
||||||
|
def on_user_joined(user, bot):
|
||||||
# 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."""
|
|
||||||
try:
|
try:
|
||||||
event_handler = event_handlers.get(event.event_type)
|
bot_channel_id = bot.ttclient.get_my_channel_id()
|
||||||
if event_handler:
|
if user.channelid == bot_channel_id:
|
||||||
event_handler(event, ttclient)
|
welcome_msg = f"👋 Hello {user.nickname}, welcome to the channel!"
|
||||||
else:
|
bot.send_channel_message(welcome_msg)
|
||||||
logging.info(f"No handler for event type: {event.event_type.name}")
|
|
||||||
except Exception as e:
|
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}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user