Commit 3fbafc0b authored by Abinash Anand's avatar Abinash Anand
Browse files

[DEV-Abinash] Integrated Language Detection

parent 445ae311
--extra-index-url https://download.pytorch.org/whl/cpu --extra-index-url https://download.pytorch.org/whl/cpu
telethon telethon
python-dotenv python-dotenv
transformers==4.44.2 transformers>=4.50.0,<5.0
torch==2.5.1+cpu torch==2.9.1+cpu
accelerate==0.33.0 accelerate==0.33.0
# python # python
import asyncio import asyncio
import csv import csv
import os
import json import json
import os
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv,find_dotenv from dotenv import load_dotenv, find_dotenv
from datetime import datetime from datetime import datetime
from langdetect import detect
from telethon import TelegramClient, events from telethon import TelegramClient, events
from telethon.errors import UserAlreadyParticipantError from telethon.errors import UserAlreadyParticipantError
...@@ -17,6 +18,7 @@ from telethon.tl.types import InputChannel, Channel, ReactionEmoji, ReactionCust ...@@ -17,6 +18,7 @@ from telethon.tl.types import InputChannel, Channel, ReactionEmoji, ReactionCust
from transformers import pipeline from transformers import pipeline
load_dotenv(find_dotenv()) load_dotenv(find_dotenv())
# load_dotenv(dotenv_path=r"{Path for the env file}")
# Please create a .env file. Add the environment variables over there. And never the env file on github or gitlab. # Please create a .env file. Add the environment variables over there. And never the env file on github or gitlab.
API_ID = int(os.getenv("API_ID")) API_ID = int(os.getenv("API_ID"))
...@@ -24,19 +26,18 @@ API_HASH = os.getenv("API_HASH") ...@@ -24,19 +26,18 @@ API_HASH = os.getenv("API_HASH")
SESSION = os.getenv("SESSION") SESSION = os.getenv("SESSION")
INVITE_LINK = os.getenv("INVITE_LINK") INVITE_LINK = os.getenv("INVITE_LINK")
sentiment_analyzer = pipeline("sentiment-analysis")
DATA_DIR = Path(os.getenv("DATA_DIR", "/app/data")) # These are the headers for the csv file.
DATA_DIR.mkdir(parents=True, exist_ok=True) HEADER = ["chat_id", "message_id", "sender_id", "timestamp", "text",
"has_media", "media_type", "file_name", "file_size_kb",
"views", "reactions", "language", "sentiment"]
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
JSON_PATH = DATA_DIR / f"messages_{timestamp}.json"
sentiment_analyzer = pipeline("sentiment-analysis") JSON_PATH = Path(f"output/messages_{timestamp}.json")
# These are the headers for the Json file. # JSON_PATH = Path(r"messages_.json") # e.g., "output/messages.json"
HEADER = ["chat_id", "message_id", "sender_id", "timestamp", "text",
"has_media", "media_type", "file_name", "file_size_kb",
"views", "reactions", "sentiment"]
async def list_joined_channels(client): async def list_joined_channels(client):
channels = [] channels = []
...@@ -47,7 +48,7 @@ async def list_joined_channels(client): ...@@ -47,7 +48,7 @@ async def list_joined_channels(client):
return channels return channels
async def process_messages_for_all_channels(client, process_message, limit): async def process_last_messages_for_all(client, process_message, limit):
channels = await list_joined_channels(client) channels = await list_joined_channels(client)
print(f"No INVITE_URL provided. Listening to all chats. Found {len(channels)} channels.") print(f"No INVITE_URL provided. Listening to all chats. Found {len(channels)} channels.")
...@@ -65,17 +66,12 @@ async def process_messages_for_all_channels(client, process_message, limit): ...@@ -65,17 +66,12 @@ async def process_messages_for_all_channels(client, process_message, limit):
async def handle_all(event): async def handle_all(event):
await process_message(event.message) await process_message(event.message)
@client.on(events.MessageEdited()) return channels
async def handle_edit(event):
message = event.message
await process_message(message)
return channels # optional, if you need the list
async def process_messages_for_one_channel(client, process_message, target, limit): async def process_last_message_for_one_channel(client, process_message, target, limit):
msgs = [msg async for msg in client.iter_messages(target, limit=limit)] msgs = [msg async for msg in client.iter_messages(target, limit=10)]
for m in reversed(msgs): for m in reversed(msgs):
await process_message(m) await process_message(m)
...@@ -100,61 +96,84 @@ def write_csv_row(row_dict): ...@@ -100,61 +96,84 @@ def write_csv_row(row_dict):
writer.writeheader() writer.writeheader()
writer.writerow(row_dict) writer.writerow(row_dict)
# def write_json_entry(entry):
def safe_serialize_reactions(message): # """Append a single JSON entry to a file."""
r = getattr(message, "reactions", None) # try:
if not r or not getattr(r, "results", None): # existing_data = []
return "" # if os.path.exists(JSON_PATH):
parts = [] # with open(JSON_PATH, "r", encoding="utf-8-sig") as f:
for rc in r.results: # try:
label = render_reaction_label(rc.reaction) # existing_data = json.load(f)
parts.append(f"{label}:{rc.count}") # except json.JSONDecodeError:
return ";".join(parts) # existing_data = []
# existing_data.append(entry)
# with open(JSON_PATH, "w", encoding="utf-8") as f:
# json.dump(existing_data, f, ensure_ascii=False, indent=4)
# except Exception as e:
# print(f"JSON write error: {e}")
def detect_language(text):
"""Detect language and map it to short format."""
if not text or not text.strip():
return "Unknown"
try:
lang_code = detect(text)
lang_map = {
'en': 'En', 'de': 'De', 'fr': 'Fr', 'es': 'Es', 'it': 'It',
'pt': 'Pt', 'nl': 'Nl', 'ru': 'Ru', 'zh-cn': 'Zh', 'ja': 'Ja',
'ar': 'Ar', 'hi': 'Hi'
}
return lang_map.get(lang_code, lang_code.upper())
except Exception:
return "Unknown"
def write_json_entry_per_post(entry, message_id, sender_id): def write_json_entry_per_post(entry, message_id, sender_id):
"""Create a separate JSON file for each post.""" """Create a separate JSON file for each post."""
try: try:
JSON_PATH = DATA_DIR / f"output/post_{message_id}_{sender_id}.json" os.makedirs("output", exist_ok=True)
JSON_PATH.parent.mkdir(parents=True, exist_ok=True) # <-- ADD THIS LINE ✅ json_path = Path(f"output/post_{message_id}_{sender_id}.json")
existing_data = [] with open(json_path, "w", encoding="utf-8-sig") as f:
if JSON_PATH.exists(): json.dump(entry, f, ensure_ascii=False, indent=4)
with open(JSON_PATH, "r", encoding="utf-8-sig") as f: print(f"JSON file saved: {json_path}")
try:
existing_data = json.load(f)
except json.JSONDecodeError:
existing_data = []
existing_data.append(entry)
with open(JSON_PATH, "w", encoding="utf-8") as f:
json.dump(existing_data, f, ensure_ascii=False, indent=4)
print(f"JSON file saved: {JSON_PATH}")
except Exception as e: except Exception as e:
print(f"JSON write error: {e}")
print(f"Error writing JSON for post {message_id}: {e}") print(f"Error writing JSON for post {message_id}: {e}")
def write_text_file_per_post(message,sender_id): def write_text_file_per_post(message,sender_id):
"""Save only the text of a message to a separate .txt file.""" """Save only the text of a message to a separate .txt file."""
try: try:
FILE_PATH = DATA_DIR / f"output/post_{message.id}_{sender_id}.txt" os.makedirs("output", exist_ok=True)
FILE_PATH.parent.mkdir(parents=True, exist_ok=True)
text_content = message.text or "" text_content = message.text or ""
if not text_content.strip(): if not text_content.strip():
return # skip empty or non-text messages return # skip empty or non-text messages
with open(FILE_PATH, "w", encoding="utf-8-sig") as f:
file_path = Path(f"output/post_{message.id}_{sender_id}.txt")
with open(file_path, "w", encoding="utf-8-sig") as f:
f.write(text_content.strip()) f.write(text_content.strip())
print(f"Text file saved: {FILE_PATH}") print(f"Text file saved: {file_path}")
except Exception as e: except Exception as e:
print(f"Error writing text file for post {message.id}: {e}") print(f"Error writing text file for post {message.id}: {e}")
async def process_message_for_json(message): def safe_serialize_reactions(message):
r = getattr(message, "reactions", None)
if not r or not getattr(r, "results", None):
return ""
parts = []
for rc in r.results:
label = render_reaction_label(rc.reaction)
parts.append(f"{label}:{rc.count}")
return ";".join(parts)
async def process_message_for_csv(message):
sentiment = await asyncio.to_thread(sentiment_analyzer, message.text or "") sentiment = await asyncio.to_thread(sentiment_analyzer, message.text or "")
sender_id = getattr(message, "sender_id", None) sender_id = getattr(message, "sender_id", None)
language = detect_language(message.text or "")
row = { row = {
"chat_id": message.chat_id, "chat_id": message.chat_id,
"message_id": message.id, "message_id": message.id,
"sender_id": sender_id, "sender_id": getattr(message, "sender_id", None),
"timestamp": message.date.isoformat() if getattr(message, "date", None) else None, "timestamp": message.date.isoformat() if getattr(message, "date", None) else None,
"text": (message.text[:3000] if message.text else ""), "text": (message.text[:3000] if message.text else ""),
"has_media": bool(message.media), "has_media": bool(message.media),
...@@ -163,14 +182,15 @@ async def process_message_for_json(message): ...@@ -163,14 +182,15 @@ async def process_message_for_json(message):
"file_size_kb": round(getattr(message.file, "size", 0) / 1024, 2) if getattr(message, "file", None) else "", "file_size_kb": round(getattr(message.file, "size", 0) / 1024, 2) if getattr(message, "file", None) else "",
"views": getattr(message, "views", None), "views": getattr(message, "views", None),
"reactions": safe_serialize_reactions(message), "reactions": safe_serialize_reactions(message),
"language" : language,
"sentiment": sentiment[0]["label"] if sentiment else "" "sentiment": sentiment[0]["label"] if sentiment else ""
} }
try: try:
#write_csv_row(row) # write_csv_row(row)
write_json_entry_per_post(row, message.id, sender_id) write_json_entry_per_post(row, message.id, sender_id)
write_text_file_per_post(message,sender_id) write_text_file_per_post(message,sender_id)
except Exception as e: except Exception as e:
print(f"CSV write error: {e}") print(f"JSON write error: {e}")
def render_reaction_label(r): def render_reaction_label(r):
if isinstance(r, ReactionEmoji): if isinstance(r, ReactionEmoji):
...@@ -211,7 +231,14 @@ async def ensure_joined(client, invite_url: str): ...@@ -211,7 +231,14 @@ async def ensure_joined(client, invite_url: str):
pass pass
return entity return entity
async def process_message(message): async def main():
client = TelegramClient(SESSION, API_ID, API_HASH)
await client.start()
# Resolve and ensure we are re in the channel/group
target = await ensure_joined(client, INVITE_LINK)
async def process_message(message):
# 1. Basic Info # 1. Basic Info
print("--- New Message ---") print("--- New Message ---")
...@@ -240,22 +267,16 @@ async def process_message(message): ...@@ -240,22 +267,16 @@ async def process_message(message):
print(f" - {label}: {reaction_count.count}") print(f" - {label}: {reaction_count.count}")
print("------\n") print("------\n")
await process_message_for_json(message) await process_message_for_csv(message)
async def main():
client = TelegramClient(SESSION, API_ID, API_HASH)
await client.start()
# Resolve and ensure we are re in the channel/group
target = await ensure_joined(client, INVITE_LINK)
if target: if target:
print("Listening to single channel ") print("Listening to single channel ")
await process_messages_for_one_channel(client, process_message, target, limit= 5) await process_last_message_for_one_channel(client, process_message, target, limit= 5)
# After collecting
else: else:
print("No INVITE_URL provided. Listening to all chats.") print("No INVITE_URL provided. Listening to all chats.")
await process_messages_for_all_channels(client, process_message, limit=5) await process_last_messages_for_all(client, process_message, limit=5)
print("Listening for messages...") print("Listening for messages...")
await client.run_until_disconnected() await client.run_until_disconnected()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment