AkshayHuggingFace commited on
Commit
f2c1750
·
verified ·
1 Parent(s): 28eb19e

Telegram bot

Browse files
Files changed (10) hide show
  1. .env +3 -0
  2. .gitignore +4 -0
  3. README.md +73 -13
  4. agent.py +33 -0
  5. db_utils.py +78 -0
  6. echobot.py +191 -0
  7. requirements.txt +4 -0
  8. runner.py +52 -0
  9. tasks.db +0 -0
  10. tools.py +14 -0
.env ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ GOOGLE_API_KEY=AIzaSyCPF-QQ04wj6GdY9zIWqW46Z4HlXpccpOg
2
+ GOOGLE_GENAI_USE_VERTEXAI=False
3
+ TELEGRAM_BOT_TOKEN=8171811430:AAFlatEQrPmD7K4-2XHDlHDPVz1-Q4gCbms
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ __pycache__/
2
+ venv/
3
+ .env
4
+ tasks.db
README.md CHANGED
@@ -1,13 +1,73 @@
1
- ---
2
- title: Example Example
3
- emoji: 🌖
4
- colorFrom: red
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
- pinned: false
10
- short_description: Exapmke app
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PROJECT TITLE
2
+ **Task Manager Telegram bot:** An AI-powered Telegram bot that helps users add, edit, list, and delete tasks seamlessly using natural language or commands.
3
+
4
+ ---
5
+
6
+ ### PROJECT DESCRIPTION:
7
+ Task Manager Telegram Bot is an AI-powered Telegram bot designed to help users manage their tasks efficiently. It allows users to:
8
+
9
+ - Add new tasks using commands or free text.
10
+ - Edit existing tasks with ease.
11
+ - List all current tasks in an organized format.
12
+ - Delete tasks individually with confirmation.
13
+ - Keep track of task history and previous conversations.
14
+ - Interact naturally using both commands and conversational language.
15
+
16
+ ---
17
+
18
+ ### TECHNOLOGIES USED:
19
+
20
+ - **PYTHON:** The core programming language used to build the Telegram bot and implement all task management functionalities.
21
+
22
+ - **PYTHON-TELEGRAM-BOT:** A Python library used to interact with the Telegram Bot API for sending and receiving messages.
23
+
24
+ - **SQLALCHEMY & SQLITE:** Used for storing user tasks and chat history in a lightweight relational database.
25
+
26
+ - **GOOGLE GEMINI (via gen-ai):** Provides AI-powered natural language understanding for interpreting free-text task commands and generating smart responses.
27
+
28
+ - **GOOGLE ADK AGENT:** Used to create an intelligent agent that maps user messages to task management actions, enabling context-aware task handling and conversational AI capabilities.
29
+
30
+ ---
31
+ ### HOW TO GET TELEGRAM TOKEN:
32
+ - Open Telegram and search for BotFather.
33
+ - Start a chat with BotFather using the /start command.
34
+ - Create a new bot using the /newbot command.
35
+ - Follow the prompts to provide a name and username for your bot.
36
+ - After successful creation, BotFather will give you a bot token.
37
+
38
+ ---
39
+
40
+ ### HOW TO INSTALL AND RUN PROJECT:
41
+ - Ensure you have Python installed on your system.
42
+ - Clone this repository to your local machine.
43
+ ```bash
44
+ git clone <repository>
45
+ - Open the project folder in your preferred code editor (e.g., VS Code).
46
+ -Create a .env file in the root directory to store your Gemini API key:
47
+ ```bash
48
+ API_KEY=your_gemini_api_key
49
+ TELEGRAM_BOT_KEY=your_telegram_token
50
+ - Navigate to the project directory in your terminal.
51
+ - Create virtual environment and activate it:
52
+ ```bash
53
+ python -m venv venv
54
+ venv\Scripts\activate
55
+ - Install the dependancies:
56
+ ```bash
57
+ pip install -r requirements.txt
58
+ - Run the application:
59
+ ```bash
60
+ python echobot.py
61
+ -----
62
+ ### HOW TO USE THE PROJECT:
63
+
64
+ - Open Telegram and search for your bot by its username.
65
+
66
+ - Start a chat with the bot by clicking Start.
67
+
68
+ - You can interact with the bot in two ways:
69
+ - Commands: Use predefined commands like /newtask, /listtask, /edittask, /deletetask to manage your tasks.
70
+ - Free text: Simply type messages like “remind me to buy milk” or “delete my meeting task”, and the bot will understand and perform the action.
71
+ - The bot will respond with confirmations, task lists, or error messages as needed.
72
+ ---
73
+
agent.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.adk.agents import Agent
2
+ from tools import add_task, list_tasks, edit_task, delete_task
3
+
4
+ task_manager_agent = Agent(
5
+ name="task_manager_agent",
6
+ model="gemini-2.5-flash",
7
+ instruction = """
8
+ # TaskManagerAgent: Smart Telegram Task Assistant
9
+
10
+ ## Persona
11
+ You are a task manager assistant. Users interact with you through **commands** or **free text**.
12
+ Your job is to always map user requests into the correct task management action.
13
+ Take the telegram user_id from runner and if the user enters any tasks or performs any operations update everything to that user_id.
14
+
15
+ ## Core Rules
16
+ 1. If user sends a command (`/newtask`, `/listtask`, `/edittask`, `/deletetask`), use the tool directly.
17
+ 2. If user sends free text, interpret and call the matching tool:
18
+ - "remind me to buy milk" → call /newtask with task="buy milk".
19
+ - "show my tasks" → call /listtask.
20
+ - "delete my meeting task" → call /deletetask with task="meeting".
21
+ - "change drink juice to drink green tea" → call /edittask with old="drink juice", new="drink green tea".
22
+ 3. Always reply with a clear confirmation message:
23
+ - ✅ Task added: <task>
24
+ - 📋 Your tasks: <list>
25
+ - 🗑️ Task deleted: <task>
26
+ - ✏️ Task updated: <old → new>
27
+ 4. If request is unclear, ask for clarification instead of failing.
28
+ 5. Never reply with “Sorry, I didn't understand”.
29
+ """
30
+ ,
31
+
32
+ tools=[add_task, list_tasks, edit_task, delete_task]
33
+ )
db_utils.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine, Column, Integer, String, DateTime
2
+ from sqlalchemy.orm import sessionmaker, declarative_base
3
+ from datetime import datetime
4
+
5
+ Base = declarative_base()
6
+
7
+ class ChatMessage(Base):
8
+ __tablename__ = "chat_messages"
9
+ id = Column(Integer, primary_key=True, index=True)
10
+ user_id = Column(String, index=True)
11
+ role = Column(String) # "user" or "bot"
12
+ message = Column(String)
13
+ timestamp = Column(DateTime, default=datetime.utcnow)
14
+
15
+ class Task(Base):
16
+ __tablename__ = "tasks"
17
+ id = Column(Integer, primary_key=True, index=True)
18
+ user_id = Column(String, index=True)
19
+ name = Column(String)
20
+ timestamp = Column(DateTime, default=datetime.utcnow)
21
+
22
+ engine = create_engine("sqlite:///tasks.db", echo=False)
23
+ SessionLocal = sessionmaker(bind=engine)
24
+
25
+ def init_db():
26
+ Base.metadata.create_all(bind=engine)
27
+
28
+ def save_message(user_id, role, message):
29
+ session = SessionLocal()
30
+ msg = ChatMessage(user_id=user_id, role=role, message=message)
31
+ session.add(msg)
32
+ session.commit()
33
+ session.close()
34
+
35
+ def load_chat_history(user_id, limit=5):
36
+ session = SessionLocal()
37
+ messages = session.query(ChatMessage).filter_by(user_id=user_id).order_by(ChatMessage.timestamp.desc()).limit(limit).all()
38
+ session.close()
39
+ return reversed(messages)
40
+
41
+ # --- Task DB functions ---
42
+ def add_task_db(user_id, task_name):
43
+ session = SessionLocal()
44
+ task = Task(user_id=user_id, name=task_name)
45
+ session.add(task)
46
+ session.commit()
47
+ session.close()
48
+ return f'Task "{task_name}" added successfully ✅'
49
+
50
+ def list_tasks_db(user_id):
51
+ session = SessionLocal()
52
+ tasks = session.query(Task).filter_by(user_id=user_id).order_by(Task.timestamp.asc()).all()
53
+ session.close()
54
+ if not tasks:
55
+ return "No tasks found."
56
+ return "Your Tasks:\n" + "\n".join([f"{i+1}. {t.name}" for i, t in enumerate(tasks)])
57
+
58
+ def edit_task_db(user_id, old_name, new_name):
59
+ session = SessionLocal()
60
+ task = session.query(Task).filter_by(user_id=user_id, name=old_name).first()
61
+ if task:
62
+ task.name = new_name
63
+ session.commit()
64
+ session.close()
65
+ return f'Task "{old_name}" updated to "{new_name}" ✅'
66
+ session.close()
67
+ return f'Task "{old_name}" not found ❌'
68
+
69
+ def delete_task_db(user_id, task_name):
70
+ session = SessionLocal()
71
+ task = session.query(Task).filter_by(user_id=user_id, name=task_name).first()
72
+ if task:
73
+ session.delete(task)
74
+ session.commit()
75
+ session.close()
76
+ return f'Task "{task_name}" deleted successfully 🗑️'
77
+ session.close()
78
+ return f'Task "{task_name}" not found ❌'
echobot.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import logging
3
+ import asyncio
4
+ from dotenv import load_dotenv
5
+ import os
6
+ from telegram import Update, ForceReply
7
+ from telegram.ext import (
8
+ Application, CommandHandler, MessageHandler, ContextTypes, filters, ConversationHandler
9
+ )
10
+ from tools import add_task, list_tasks, edit_task, delete_task
11
+ from runner import create_session_async, run_agent
12
+ from db_utils import save_message, load_chat_history, init_db
13
+
14
+ logging.basicConfig(
15
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
16
+ )
17
+ logger = logging.getLogger(__name__)
18
+
19
+ load_dotenv()
20
+ session = None
21
+
22
+ NEW_TASK, EDIT_TASK_NAME, EDIT_TASK_NEW_NAME, DELETE_TASK_NAME, DELETE_TASK_CONFIRM = range(5)
23
+
24
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
25
+ menu = (
26
+ "Welcome to Task Manager Bot!\n"
27
+ "Choose a method:\n"
28
+ "/newtask - Add a new task\n"
29
+ "/listtask - View all tasks\n"
30
+ "/edittask - Edit an existing task\n"
31
+ "/deletetask - Delete a task\n"
32
+ "/history - Show last 5 chats"
33
+ )
34
+ await update.message.reply_text(menu, reply_markup=ForceReply(selective=True))
35
+
36
+
37
+ async def newtask_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
38
+ await update.message.reply_text("Enter task name:")
39
+ return NEW_TASK
40
+
41
+ async def newtask_save(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
42
+ user_id = str(update.effective_user.id)
43
+ task_name = update.message.text.strip()
44
+ if task_name:
45
+ result = add_task(user_id, task_name)
46
+ await update.message.reply_text(result)
47
+ else:
48
+ await update.message.reply_text("Task name cannot be empty. Please try again.")
49
+ return NEW_TASK
50
+ return ConversationHandler.END
51
+
52
+ async def listtask(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
53
+ user_id = str(update.effective_user.id)
54
+ result = list_tasks(user_id)
55
+ await update.message.reply_text(result)
56
+
57
+ async def edittask_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
58
+ await update.message.reply_text("Enter the task name you want to edit:")
59
+ return EDIT_TASK_NAME
60
+
61
+ async def edittask_get_new_name(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
62
+ user_id = str(update.effective_user.id)
63
+ old_name = update.message.text.strip()
64
+ context.user_data['edit_task_old_name'] = old_name
65
+
66
+ tasks_list = list_tasks(user_id)
67
+ if old_name not in tasks_list:
68
+ await update.message.reply_text("Task not found. Please enter a valid task name:")
69
+ return EDIT_TASK_NAME
70
+ await update.message.reply_text("Enter the new task name:")
71
+ return EDIT_TASK_NEW_NAME
72
+
73
+ async def edittask_save(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
74
+ user_id = str(update.effective_user.id)
75
+ new_name = update.message.text.strip()
76
+ old_name = context.user_data.get('edit_task_old_name')
77
+ result = edit_task( user_id, old_name, new_name)
78
+ await update.message.reply_text(result)
79
+ return ConversationHandler.END
80
+
81
+ async def deletetask_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
82
+ await update.message.reply_text("Enter the task name you want to delete:")
83
+ return DELETE_TASK_NAME
84
+
85
+ async def deletetask_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
86
+ user_id = str(update.effective_user.id)
87
+ task_name = update.message.text.strip()
88
+ context.user_data['delete_task_name'] = task_name
89
+ tasks_list = list_tasks(user_id)
90
+ if task_name not in tasks_list:
91
+ await update.message.reply_text("Task not found. Please enter a valid task name:")
92
+ return DELETE_TASK_NAME
93
+ await update.message.reply_text(f'Are you sure you want to delete "{task_name}"? If yes, type "yes" else type /cancel')
94
+ return DELETE_TASK_CONFIRM
95
+
96
+ async def deletetask_finish(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
97
+ user_id = str(update.effective_user.id)
98
+ response = update.message.text.strip().lower()
99
+ task_name = context.user_data.get('delete_task_name')
100
+ if response == "yes":
101
+ result = delete_task(user_id, task_name)
102
+ await update.message.reply_text(result)
103
+ else:
104
+ await update.message.reply_text("Delete cancelled ❌")
105
+ return ConversationHandler.END
106
+
107
+
108
+ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
109
+ await update.message.reply_text("Cancelled ❌")
110
+ return ConversationHandler.END
111
+
112
+ async def history(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
113
+ user_id = str(update.effective_user.id)
114
+ messages = load_chat_history(user_id, limit=5)
115
+ if not messages:
116
+ await update.message.reply_text("No history yet.")
117
+ return
118
+ history_text = "\n".join([f"[{m.role}] {m.message}" for m in messages])
119
+ await update.message.reply_text("🕑 Your recent chats:\n\n" + history_text)
120
+
121
+ async def handle_free_text(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
122
+ user_id = int(update.effective_user.id)
123
+ user_message = update.message.text.strip()
124
+ save_message(user_id, "user", user_message)
125
+ await update.message.reply_text(f"🤖 Thinking... {user_id}")
126
+ try:
127
+ response = await run_agent(user_id, session.id, user_message)
128
+ if not response:
129
+ response_text = "⚠️ I didn't get any response."
130
+ elif isinstance(response, dict):
131
+ response_text = response.get("text") or response.get("message") or str(response)
132
+ else:
133
+ response_text = str(response).strip()
134
+ # if not response_text:
135
+ # response_text = "⚠️ Sorry, I got an empty reply."
136
+
137
+ save_message(user_id, "bot", response_text)
138
+ await update.message.reply_text(response_text)
139
+
140
+ except Exception as e:
141
+ await update.message.reply_text(f"❌ Error: {str(e)}")
142
+
143
+
144
+
145
+ def main():
146
+ global session
147
+ init_db()
148
+ loop = asyncio.get_event_loop()
149
+ session = loop.run_until_complete(create_session_async())
150
+ telegram_token = os.getenv("TELEGRAM_BOT_TOKEN")
151
+ application = Application.builder().token(telegram_token).build()
152
+ application.add_handler(CommandHandler("start", start))
153
+ application.add_handler(CommandHandler("listtask", listtask))
154
+ application.add_handler(CommandHandler("history", history))
155
+
156
+ newtask_conv = ConversationHandler(
157
+ entry_points=[CommandHandler("newtask", newtask_start)],
158
+ states={NEW_TASK: [MessageHandler(filters.TEXT & ~filters.COMMAND, newtask_save)]},
159
+ fallbacks=[]
160
+ )
161
+ application.add_handler(newtask_conv)
162
+
163
+ edittask_conv = ConversationHandler(
164
+ entry_points=[CommandHandler("edittask", edittask_start)],
165
+ states={
166
+ EDIT_TASK_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, edittask_get_new_name)],
167
+ EDIT_TASK_NEW_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, edittask_save)]
168
+ },
169
+ fallbacks=[]
170
+ )
171
+ application.add_handler(edittask_conv)
172
+
173
+ deletetask_conv = ConversationHandler(
174
+ entry_points=[CommandHandler("deletetask", deletetask_start)],
175
+ states={
176
+ DELETE_TASK_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, deletetask_confirm)],
177
+ DELETE_TASK_CONFIRM: [MessageHandler(filters.TEXT & ~filters.COMMAND, deletetask_finish)]
178
+ },
179
+ fallbacks=[CommandHandler("cancel", cancel)]
180
+ )
181
+ application.add_handler(deletetask_conv)
182
+ application.add_handler(CommandHandler("cancel", cancel))
183
+
184
+ application.add_handler(
185
+ MessageHandler(filters.TEXT & ~filters.COMMAND, handle_free_text)
186
+ )
187
+ application.run_polling()
188
+
189
+ if __name__ == "__main__":
190
+ main()
191
+
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ google-adk==1.7.0
2
+ requests
3
+ python-telegram-bot
4
+
runner.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from dotenv import load_dotenv
3
+ from google.adk.runners import InMemoryRunner
4
+ from google.genai import types
5
+ from agent import task_manager_agent
6
+
7
+ load_dotenv()
8
+
9
+ runner = InMemoryRunner(
10
+ agent=task_manager_agent,
11
+ app_name="task_manager_app")
12
+
13
+ async def create_session_async():
14
+ session = await runner.session_service.create_session(
15
+ app_name="task_manager_app", user_id="user"
16
+ )
17
+ return session
18
+
19
+
20
+ async def run_agent(user_id:int, session_id: str, user_message: str):
21
+ content = types.Content(role="user", parts=[types.Part.from_text(text=user_message)]) + f"user id : {user_id}"
22
+ response_text = ""
23
+ for event in runner.run(
24
+ user_id=user_id,
25
+ session_id=session_id,
26
+ new_message=content,
27
+ ):
28
+
29
+ if event.content.parts and event.content.parts[0].text:
30
+ response_text += event.content.parts[0].text + "\n"
31
+ return response_text.strip()
32
+ # Simple main function to test run_agent interactively
33
+ import sys
34
+
35
+ def main():
36
+ import asyncio
37
+ user_id = "user" # For testing, you can change this to any string
38
+ loop = asyncio.get_event_loop()
39
+ session = loop.run_until_complete(create_session_async())
40
+ print("ADK Agent Test Console. Type 'exit' to quit.")
41
+ while True:
42
+ user_message = input("You: ")
43
+ if user_message.lower() == "exit":
44
+ break
45
+ response = loop.run_until_complete(run_agent(user_id, session.id, user_message))
46
+ print(f"Agent: {response}\n")
47
+
48
+ if __name__ == "__main__":
49
+ main()
50
+
51
+
52
+
tasks.db ADDED
Binary file (28.7 kB). View file
 
tools.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from db_utils import add_task_db, list_tasks_db, edit_task_db, delete_task_db
3
+
4
+ def add_task(user_id: int, task_name: str) -> str:
5
+ return add_task_db(user_id, task_name)
6
+
7
+ def list_tasks(user_id: int) -> str:
8
+ return list_tasks_db(user_id)
9
+
10
+ def edit_task(user_id: int, old_name: str, new_name: str) -> str:
11
+ return edit_task_db(user_id, old_name, new_name)
12
+
13
+ def delete_task(user_id: int, task_name: str) -> str:
14
+ return delete_task_db(user_id, task_name)