import os
import datetime
import pytz
import json
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, MessageHandler, filters, ContextTypes
from telethon.sync import TelegramClient
# --- Configuration ---
# It's highly recommended to use environment variables for sensitive data
BOT_TOKEN = os.environ.get("BOT_TOKEN", "YOUR_BOT_TOKEN")
API_ID = int(os.environ.get("API_ID", "YOUR_API_ID"))
API_HASH = os.environ.get("API_HASH", "YOUR_API_HASH")
SESSION_NAME = "telegram_bot_session"
SOURCE_CHANNELS = {
'samvadah': 'samvadah',
'ramdootah': 'ramdootah'
}
USER_DATA_FILE = "user_data.json"
AVOID_MESSAGE_ID = 8793 # The ID of the specific message to avoid from samvadah
VAKYABHYAS_START_DATE = datetime.datetime(2024, 2, 8, tzinfo=pytz.UTC)
# --- Logging ---
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
# --- User Data Management ---
def get_default_user_config():
"""Returns the default configuration for a new user."""
return {
"daily_dose": True,
"news_radio": True,
"custom_hashtags": {
"#Gita": {"count": 1, "time": "05:00"},
"#Quote": {"count": 1, "time": "10:00"},
"#Subhashitam": {"count": 1, "time": "12:00"},
"#Vakyabhyas": {"count": 1, "time": "14:00"},
"#Celebrating_Sanskrit": {"count": 1, "time": "15:00"},
"#Sanskritlessons": {"count": 1, "time": "16:00"},
"#hasya": {"count": 1, "time": "17:00"},
"#Viloma_Kavya": {"count": 1, "time": "18:00"}
},
"news_filter": {
"avoid_hashtags": {
"#Akashvani": True, "#AkashVani_Text": True, "#Sudharma": True,
"#Vruttantah": True, "#newsinsanskrit": True, "#newsaudio": True,
"#saptahiki": True, "#mannkibaat": True, "#Panchangam": True, "#Career": True
},
"avoid_youtube": True,
"avoid_pdfs": True # New setting
},
"timezone": "UTC",
"state": None # To manage conversation state, e.g., 'awaiting_timezone'
}
def load_user_data():
try:
with open(USER_DATA_FILE, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
def save_user_data(data):
with open(USER_DATA_FILE, "w") as f:
json.dump(data, f, indent=4)
user_data = load_user_data()
# --- Telethon Client ---
# Using a context manager for the client is better for production
client = TelegramClient(SESSION_NAME, API_ID, API_HASH)
# --- Bot Handlers ---
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = str(update.effective_chat.id)
if user_id not in user_data:
logger.info(f"New user or chat: {user_id}")
user_data[user_id] = get_default_user_config()
save_user_data(user_data)
await show_main_menu(update, context)
async def show_main_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Displays the main menu."""
user_id = str(update.effective_chat.id)
config = user_data.get(user_id, get_default_user_config())
keyboard = [
[InlineKeyboardButton(f"Daily dose {'✓' if config['daily_dose'] else '✗'}", callback_data='toggle_daily_dose')],
[InlineKeyboardButton(f"News and radio {'✓' if config['news_radio'] else '✗'}", callback_data='toggle_news_radio')],
[InlineKeyboardButton("Customise", callback_data='customise')],
[InlineKeyboardButton(f"Time Zone ({config['timezone']})", callback_data='set_timezone')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
text = "Welcome! Manage your content subscriptions below."
if update.callback_query:
await update.callback_query.edit_message_text(text, reply_markup=reply_markup)
else:
await update.message.reply_text(text, reply_markup=reply_markup)
async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Parses all button presses."""
query = update.callback_query
await query.answer()
user_id = str(query.message.chat_id)
# Simple Toggles
if query.data == 'toggle_daily_dose':
user_data[user_id]['daily_dose'] = not user_data[user_id]['daily_dose']
save_user_data(user_data)
await show_main_menu(update, context)
elif query.data == 'toggle_news_radio':
user_data[user_id]['news_radio'] = not user_data[user_id]['news_radio']
save_user_data(user_data)
await show_main_menu(update, context)
# TODO: Add handlers for 'customise', 'set_timezone', and deeper menus
# This part can get very complex with many states and callbacks.
# For now, this structure is ready to be expanded.
# --- Content Forwarding Logic ---
async def fetch_and_forward_ramdootah(context: ContextTypes.DEFAULT_TYPE):
"""Checks ramdootah and forwards new messages based on user preferences."""
logger.info("Job started: fetch_and_forward_ramdootah")
# In a real app, you'd store the ID of the last message forwarded per channel
# to avoid sending duplicates. For example, in a simple file or database.
last_id = 0 # Placeholder
async with client:
channel_entity = await client.get_entity(SOURCE_CHANNELS['ramdootah'])
# Get recent messages, newer than the last one we processed
messages = await client.get_messages(channel_entity, min_id=last_id, limit=20)
if not messages:
logger.info("No new messages from ramdootah.")
return
for message in reversed(messages): # Process oldest to newest
for user_id, config in user_data.items():
if not config.get("news_radio", False):
continue
# --- Filtering Logic ---
should_forward = True
msg_text = message.text.lower() if message.text else ""
filters = config.get("news_filter", {})
# 1. Filter by PDF
if filters.get("avoid_pdfs", False) and message.document and 'application/pdf' in message.document.mime_type:
should_forward = False
logger.info(f"Skipping PDF for user {user_id}")
# 2. Filter by YouTube
if should_forward and filters.get("avoid_youtube", False) and ("youtube.com" in msg_text or "youtu.be" in msg_text):
should_forward = False
logger.info(f"Skipping YouTube link for user {user_id}")
# 3. Filter by Hashtags
if should_forward:
for hashtag, is_avoided in filters.get("avoid_hashtags", {}).items():
if is_avoided and hashtag.lower() in msg_text:
should_forward = False
logger.info(f"Skipping message with hashtag {hashtag} for user {user_id}")
break
if should_forward:
try:
# This command forwards the message with the original channel's name.
await context.bot.forward_message(
chat_id=user_id,
from_chat_id=f"@{SOURCE_CHANNELS['ramdootah']}",
message_id=message.id
)
except Exception as e:
logger.error(f"Failed to forward message {message.id} to {user_id}: {e}")
# Update last processed message ID here
# last_id = message.id
# TODO: Implement the scheduled 'fetch_and_forward_samvadah' job.
# This would be more complex, as it needs to run for each user based on their
# custom hashtag times and timezones. A simple daily job is not enough.
# You would need a job that runs every minute, checks all users, and sees if
# any scheduled post is due.
def main():
"""Start the bot."""
application = Application.builder().token(BOT_TOKEN).build()
# Handlers
application.add_handler(CommandHandler("start", start))
application.add_handler(CallbackQueryHandler(button_callback))
# --- Jobs ---
job_queue = application.job_queue
# Check for news from ramdootah every hour
# Note: IST is UTC+5:30. The range 6AM-11PM IST corresponds to approx 00:30-17:30 UTC.
# A simpler approach is to run it every hour and just forward if there's anything new.
job_queue.run_repeating(fetch_and_forward_ramdootah, interval=3600, first=10)
logger.info("Bot started and polling.")
application.run_polling()
if __name__ == '__main__':
main()
Public Last updated: 2025-11-02 10:38:31 AM