diff --git a/.gitignore b/.gitignore index ba04025..b1fde41 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ *.swp tags node_modules -__pycache__ \ No newline at end of file +__pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..81bf4f6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml diff --git a/README.md b/README.md index 653943f..50cbbd8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Introduction -With the **Chatbot Builder**, organizations can easily automate responses to time-consuming and frequently asked questions. The process is simple: +With the **Chatbot Builder**, organizations can easily automate responses to time-consuming and frequently asked questions. The process is simple: 1. **Identify Common Questions**: List the repetitive questions that your team handles. 2. **Create Reply Templates**: Design templates with variables to customize responses. @@ -75,3 +75,4 @@ You can visualize the chatbot conversation as a tree structure. Each branch repr You are now ready to design your **Chatbot Flow** and start automating your interactions with users. +mit diff --git a/chatbot/api/telegram_api.py b/chatbot/api/telegram_api.py index 6e9321f..16632aa 100644 --- a/chatbot/api/telegram_api.py +++ b/chatbot/api/telegram_api.py @@ -2,44 +2,33 @@ import requests import json from chatbot.chatbot.doctype.chatbot_log.chatbot_log import log_chatbot -from chatbot.utils import validate_user, get_root_chatbot_flow, get_associated_party_types, fetch_all_children - +from chatbot.utils import validate_user, get_root_chatbot_flow, get_associated_party_types, fetch_all_children,send_document +import hashlib +from passlib.context import CryptContext +passlibctx = CryptContext(schemes=["pbkdf2_sha256","argon2",], ) class TelegramAPI: - def __init__(self, update): + def __init__(self, update,headers): self.update = update - self.token = frappe.get_doc("Chatbot Setup").get_password('telegram_api_token') - self.base_url = f"https://api.telegram.org/bot{self.token}/" - self.reply_text = str() - self.reply_markup = {} - self.data = str() - self.chat_id=str() - print("update",update) - if "callback_query" in update: - self.callback_query =update.get('callback_query') - self.message = self.callback_query.get('message') - self.data =self.callback_query.get('data') - self.chat_id = self.message.get("chat").get("id") - self.user_name="@"+self.message.get("chat").get("username") - elif "message" in update: - self.message = update.get('message') - - if self.message: - self.chat_id = self.message.get("chat").get("id") - self.user_name = "@"+self.message["chat"]["username"] + self.headers=headers + self.validate_token=False + self._validate_token() + if self.validate_token: + self.set_default_values() + + def find_type_and_send(self): + if self.response_type == "Text": + self.send_message() else: - frappe.throw(msg="Message object not found") - - def send_message(self, text:str): + self.send_document(file_path=send_document(type=self.response_type)) + def send_message(self, text:str =None): """Send a message to a Telegram chat, with optional inline buttons.""" url = f"{self.base_url}sendMessage" payload = { "chat_id": self.chat_id, - "text": text, - "ForceReply" - "reply_markup": self.reply_markup # Inline keyboard markup + "text": self.reply_text, + "reply_markup":self.reply_markup# Inline keyboard markup } - print("self.reply_markup",self.reply_markup) response = requests.post(url, json=payload) log_chatbot( @@ -83,16 +72,16 @@ def send_photo(self, chat_id, photo_url): return response.json() - def send_document(self, chat_id, file_path): + def send_document(self, file_path): """Send a document (PDF or other file types) to a Telegram chat.""" url = f"{self.base_url}sendDocument" with open(file_path, 'rb') as file: files = {'document': file} payload = { - "chat_id": chat_id + "chat_id": self.chat_id } response = requests.post(url, files=files, data=payload) - + self.send_message() if response.status_code != 200: frappe.log_error(f"Error sending document: {response.text}", "Telegram API Error") return None @@ -112,46 +101,105 @@ def process_update(self): self.reply_text = "User not registered" self.send_message(self.reply_text) - + def _validate_token(self): + secret_token_ft=self.headers.get("X-Telegram-Bot-Api-Secret-Token") + host=self.headers.get("X-Forwarded-Host") + proto=self.headers.get("X-Forwarded-Proto") + if proto !="https": + frappe.throw("Not an https Request", frappe.PermissionError) + if host !=frappe.db.get_value("Chatbot Setup","Chatbot Setup","telegram_webhook_url").strip()[8:]: + frappe.throw("Not an Host", frappe.PermissionError) + if secret_token_ft: + secret_token = frappe.get_doc("Chatbot Setup").get_password("secret_token") + if secret_token: + random_value=hashlib.sha256(secret_token_ft.encode()) + random_value=random_value.hexdigest() + if passlibctx.verify(random_value,secret_token): + self.validate_token =True + else: + frappe.throw("Verify Error", frappe.PermissionError) + else: + frappe.throw("StFxx0n0ecre Tnmx0naoke Error", frappe.PermissionError) + else: + frappe.throw("StFxx0n0ecre Tnmx0naoke Error FT", frappe.PermissionError) def handle_message(self, party_type, party_name): """Handle incoming messages.""" # Determine chatbot flow and template + self.data=self.data if frappe.db.exists("Chatbot Flow",self.data) else None pre_chatbot_flow_name=frappe.cache.get_value(self.chat_id) + pre_chatbot_flow_name=pre_chatbot_flow_name if frappe.db.exists("Chatbot Flow",pre_chatbot_flow_name) else None + parent_flow=get_root_chatbot_flow(party_type) if self.data: - chatbot_flow = frappe.get_doc('Chatbot Flow', self.data) + + chatbot_flow = frappe.get_doc('Chatbot Flow', self.data) elif pre_chatbot_flow_name: chatbot_flow = frappe.get_doc('Chatbot Flow', pre_chatbot_flow_name) children = fetch_all_children(chatbot_flow.get('name')) - if children: + if children and self.received_reply_text and frappe.db.get_value("Chatbot Message Template",chatbot_flow.template,"is_get_value")==1: chatbot_flow = frappe.get_doc('Chatbot Flow', children[0].get("name")) else: - chatbot_flow= get_root_chatbot_flow() - - + chatbot_flow= parent_flow + + else: - chatbot_flow= get_root_chatbot_flow() - + chatbot_flow=parent_flow + associated_parties = get_associated_party_types(chatbot_flow.get('name')) children = fetch_all_children(chatbot_flow.get('name')) template = chatbot_flow.get('template') - - # Build reply markup (inline keyboard) - self.reply_markup = {"inline_keyboard": [], 'resize_keyboard': True} - - if children: - self.reply_markup["inline_keyboard"] = [ - [{"text": child.get('button_text'), "callback_data": child.get('name')}] - for child in children - ] - + chatbot_template = frappe.get_doc('Chatbot Message Template', template) + reply_markup=None + if chatbot_template.is_get_value==1: + reply_markup={"force_reply":True} + else: + inline_keyboard=[[]] + if children: + inline_keyboard= [ + [{"text": child.get('button_text'), "callback_data": child.get('name')} ] + for child in children if child.get('button_text') + ] + + if chatbot_flow.get('name') !=parent_flow.get("name"): + inline_keyboard[0].append({"text": "Main Menu", "callback_data":parent_flow.get("name") }) + reply_markup={"inline_keyboard": inline_keyboard, 'resize_keyboard': True} + self.reply_markup=reply_markup # Check if party_type is in associated parties if party_type in associated_parties: - chatbot_template = frappe.get_doc('Chatbot Message Template', template) - kwargs = {party_type.lower(): party_name} + kwargs = {party_type.lower(): party_name,"data":self.message_received} self.reply_text = chatbot_template.get_rendered_template(**kwargs) - self.send_message(self.reply_text) + self.response_type =chatbot_template.response_type + self.find_type_and_send() frappe.cache.set_value(self.chat_id,chatbot_flow.get('name')) def set_chat_id(self,party_type,party_name): - chat_id =frappe.db.get_value(party_type,party_name,"telegram_user_id") + chat_id = frappe.db.get_value(party_type,party_name,"telegram_user_id") if not chat_id or chat_id!=self.chat_id: - frappe.db.set_value(party_type,party_name,"telegram_user_id",self.chat_id) \ No newline at end of file + frappe.db.set_value(party_type,party_name,"telegram_user_id",self.chat_id) + def set_default_values(self): + self.token = frappe.get_doc("Chatbot Setup").get_password('telegram_api_token') + self.base_url = f"https://api.telegram.org/bot{self.token}/" + self.reply_text = str() + self.reply_markup = {} + self.data = str() + self.chat_id=str() + self.message_received=str() + self.received_reply_text=str() + + if "callback_query" in self.update: + self.callback_query =self.update.get('callback_query') + self.message = self.callback_query.get('message') + self.data =self.callback_query.get('data') + self.chat_id = self.message.get("chat").get("id") + self.user_name="@"+self.message.get("chat").get("username") + elif "message" in self.update: + self.message = self.update.get('message') + + if self.message: + + self.chat_id = self.message.get("chat").get("id") + self.user_name = "@"+self.message["chat"]["username"] + self.received_reply_text =self.message.get("reply_to_message") + if self.received_reply_text: + self.received_reply_text=self.received_reply_text.get("text") + self.message_received=self.message.get("text") + else: + frappe.throw(msg="Message object not found") diff --git a/chatbot/chatbot/doctype/chatbot_associated_party_types/chatbot_associated_party_types.json b/chatbot/chatbot/doctype/chatbot_associated_party_types/chatbot_associated_party_types.json index 086a8f2..5d9d11c 100644 --- a/chatbot/chatbot/doctype/chatbot_associated_party_types/chatbot_associated_party_types.json +++ b/chatbot/chatbot/doctype/chatbot_associated_party_types/chatbot_associated_party_types.json @@ -29,4 +29,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/chatbot/chatbot/doctype/chatbot_flow/chatbot_flow.json b/chatbot/chatbot/doctype/chatbot_flow/chatbot_flow.json index 313b1a5..879bb0d 100644 --- a/chatbot/chatbot/doctype/chatbot_flow/chatbot_flow.json +++ b/chatbot/chatbot/doctype/chatbot_flow/chatbot_flow.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_import": 1, "allow_rename": 1, "autoname": "field:name1", "creation": "2024-09-22 11:22:36.891954", @@ -78,26 +79,28 @@ "fieldname": "associated_party_types", "fieldtype": "Table MultiSelect", "label": "Associated Party Types", - "options": "Chatbot Associated Party Types" + "options": "Chatbot Associated Party Types", + "reqd": 1 }, { "fieldname": "template", "fieldtype": "Link", "label": "Template", - "options": "Chatbot Message Template" + "options": "Chatbot Message Template", + "reqd": 1 }, { "fieldname": "type", "fieldtype": "Select", "label": "Type", - "options": "\nButton\nText\nGet Value\nImage\nVideo\nDocument\nLocation", + "options": "\nButton\nText", "reqd": 1 } ], "index_web_pages_for_search": 1, "is_tree": 1, "links": [], - "modified": "2024-09-25 12:55:43.551793", + "modified": "2024-09-27 16:17:25.856464", "modified_by": "Administrator", "module": "Chatbot", "name": "Chatbot Flow", @@ -121,4 +124,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/chatbot/chatbot/doctype/chatbot_log/chatbot_log.json b/chatbot/chatbot/doctype/chatbot_log/chatbot_log.json index eddd891..09c846d 100644 --- a/chatbot/chatbot/doctype/chatbot_log/chatbot_log.json +++ b/chatbot/chatbot/doctype/chatbot_log/chatbot_log.json @@ -125,4 +125,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.js b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.js index c8eaeda..df25317 100644 --- a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.js +++ b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.js @@ -1,8 +1,14 @@ // Copyright (c) 2024, Aerele and contributors // For license information, please see license.txt -// frappe.ui.form.on("Chatbot Message Template", { -// refresh(frm) { - -// }, -// }); +frappe.ui.form.on("Chatbot Message Template", { + refresh(frm) { + frm.set_query("print_format", function (doc) { + return { + filters: { + "doc_type": frm.doc.default_doctype + }, + }; + }); + }, +}); diff --git a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.json b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.json index 9bc4a6c..65e0603 100644 --- a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.json +++ b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_import": 1, "allow_rename": 1, "autoname": "prompt", "creation": "2024-09-21 14:32:31.588744", @@ -7,13 +8,15 @@ "engine": "InnoDB", "field_order": [ "placeholder", - "enable_dynamic_response", "response_type", + "is_get_value", + "enable_dynamic_response", "template", + "attachment", + "default_doctype", "print_format", "default_document", "report", - "attachment", "is_custom_function", "custom_function_path", "server_script", @@ -23,10 +26,11 @@ { "fieldname": "placeholder", "fieldtype": "Data", + "hidden": 1, "label": "Placeholder" }, { - "default": "0", + "default": "1", "fieldname": "enable_dynamic_response", "fieldtype": "Check", "label": "Enable Dynamic Response" @@ -34,9 +38,10 @@ { "fieldname": "response_type", "fieldtype": "Select", - "hidden": 1, + "in_list_view": 1, "label": "Response Type", - "options": "Text\nPrint format\nReport\nImage" + "options": "\nText\nPrint format\nReport\nImage", + "reqd": 1 }, { "depends_on": "eval:doc.enable_dynamic_response", @@ -45,6 +50,7 @@ "label": "Template" }, { + "depends_on": "eval:doc.response_type == \"Print format\"", "fieldname": "print_format", "fieldtype": "Link", "label": "Print format", @@ -52,11 +58,13 @@ }, { "fieldname": "default_document", - "fieldtype": "Link", + "fieldtype": "Dynamic Link", + "hidden": 1, "label": "Default Document", - "options": "Print Format" + "options": "default_doctype" }, { + "depends_on": "eval:doc.response_type == \"Report\"", "fieldname": "report", "fieldtype": "Link", "label": "Report", @@ -89,12 +97,27 @@ { "fieldname": "html_yzlg", "fieldtype": "HTML", - "options": "
It contains the keyword arguments respective to the associated party names
\n{Party Type} = {Party Name}
\nExample: customer = \"customer_name\"
\nSo, make sure your custom function has **kwargs as a param to get values
\n\nres = {}\n\n#get the default arguments passed using form_dict.party_name\nif frappe.form_dict.customer: \n customer = frappe.form_dict.customer\n company = frappe.db.get_default('company')\n res[\"customer_name\"] = customer\n res[\"company\"] = company\n res[\"phone\"] = \"9876543218\"\n res[\"email\"] = \"kavin@aerele.in\"\n res[\"website_url\"] = \"www.aerele.in\"\n \n #response should be passed in flags.response key : value dict\n frappe.flags.response = res \n\n"
+ "options": "It contains the keyword arguments respective to the associated party names
\n{Party Type} = {Party Name}
\nExample: customer = \"customer_name\"
\nSo, make sure your custom function has **kwargs as a param to get values
\n\nres = {}\n\n#get the default arguments passed using form_dict.party_name\nif frappe.form_dict.customer: \n customer = frappe.form_dict.customer\n company = frappe.db.get_default('company')\n res[\"customer_name\"] = customer\n res[\"company\"] = company\n res[\"phone\"] = \"9876543218\"\n res[\"email\"] = \"kavin@aerele.in\"\n res[\"website_url\"] = \"www.aerele.in\"\n \n #response should be passed in flags.response key : value dict\n frappe.flags.response = res \n\n",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval:doc.response_type == \"Print format\"",
+ "fieldname": "default_doctype",
+ "fieldtype": "Link",
+ "label": "Default Doctype",
+ "mandatory_depends_on": "eval:doc.response_type == \"Print format\"",
+ "options": "DocType"
+ },
+ {
+ "default": "1",
+ "fieldname": "is_get_value",
+ "fieldtype": "Check",
+ "label": "Requires Input"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2024-09-22 19:52:04.827393",
+ "modified": "2024-09-28 15:02:14.687617",
"modified_by": "Administrator",
"module": "Chatbot",
"name": "Chatbot Message Template",
@@ -117,4 +140,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.py b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.py
index 6ad835d..3195dd9 100644
--- a/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.py
+++ b/chatbot/chatbot/doctype/chatbot_message_template/chatbot_message_template.py
@@ -18,8 +18,7 @@ def execute_server_script(self, **kwargs):
api_method = frappe.db.get_value('Server Script',
{'name':self.server_script, 'script_type':'API'},
'api_method')
-
if api_method:
response = run_script(self.server_script, **kwargs).get('response')
- return response
\ No newline at end of file
+ return response
diff --git a/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.json b/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.json
index efcae12..2771b23 100644
--- a/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.json
+++ b/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_import": 1,
"allow_rename": 1,
"autoname": "field:party_name",
"creation": "2024-09-21 23:57:22.931228",
@@ -19,7 +20,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2024-09-22 00:01:09.139990",
+ "modified": "2024-09-24 13:27:10.904021",
"modified_by": "Administrator",
"module": "Chatbot",
"name": "Chatbot Party Type",
@@ -42,4 +43,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.py b/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.py
index 0ed2568..27f2e00 100644
--- a/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.py
+++ b/chatbot/chatbot/doctype/chatbot_party_type/chatbot_party_type.py
@@ -12,28 +12,37 @@ def validate(self):
def create_customer_custom_field(party_name):
enable_telegram,enable_whatsapp,enable_slack=frappe.db.get_value("Chatbot Setup","Chatbot Setup",["enable_telegram","enable_whatsapp","enable_slack"])
+ insert_afrter_fieldname= frappe.get_meta(party_name)
+ insert_afrter_fieldname=insert_afrter_fieldname.fields[-1].get("fieldname")
custom_fields = {}
if enable_telegram:
- custom_fields.update( {
+ custom_fields = {
party_name: [
dict(
fieldname="chatbot_details",
label="Chatbot Details",
fieldtype="Tab Break",
+ insert_afrter=insert_afrter_fieldname,
),
dict(
- fieldname="custom_telegram_username",
+ fieldname="sb_chb",
+ label="",
+ fieldtype="Section Break",
+ insert_afrter="chatbot_details"
+ ),
+ dict(
+ fieldname="telegram_username",
label="Telegram Username",
fieldtype="Data",
- insert_after="chatbot_details",
+ insert_after="sb_chb",
),
dict(
fieldname="telegram_user_id",
read_only=1,
label="",
fieldtype="Data",
- insert_after="custom_telegram_username",
+ insert_after="telegram_username",
),
]
- })
- create_custom_fields(custom_fields, update=True)
\ No newline at end of file
+ }
+ create_custom_fields(custom_fields, update=True)
diff --git a/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.json b/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.json
index 6e36881..0b05379 100644
--- a/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.json
+++ b/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.json
@@ -1,6 +1,5 @@
{
"actions": [],
- "allow_rename": 1,
"creation": "2024-09-21 14:44:19.266520",
"doctype": "DocType",
"engine": "InnoDB",
@@ -11,6 +10,7 @@
"telegram_username",
"telegram_api_token",
"telegram_webhook_url",
+ "secret_token",
"whatsapp_tab",
"enable_whatsapp",
"whatsapp_title",
@@ -42,7 +42,8 @@
{
"fieldname": "telegram_username",
"fieldtype": "Data",
- "label": "Bot Username"
+ "label": "Bot Username",
+ "read_only": 1
},
{
"fieldname": "telegram_api_token",
@@ -82,12 +83,19 @@
"fieldname": "enable_slack",
"fieldtype": "Check",
"label": "Enable"
+ },
+ {
+ "fieldname": "secret_token",
+ "fieldtype": "Password",
+ "hidden": 1,
+ "no_copy": 1,
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2024-09-26 11:46:39.605839",
+ "modified": "2024-09-26 16:10:19.515181",
"modified_by": "Administrator",
"module": "Chatbot",
"name": "Chatbot Setup",
@@ -107,4 +115,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.py b/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.py
index b0976c3..92bd7c6 100644
--- a/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.py
+++ b/chatbot/chatbot/doctype/chatbot_setup/chatbot_setup.py
@@ -4,53 +4,74 @@
import frappe
from frappe.model.document import Document
import requests
-
+import secrets
+import hashlib
+from passlib.context import CryptContext
+passlibctx = CryptContext(schemes=["pbkdf2_sha256","argon2",], )
class ChatbotSetup(Document):
- def autoname(self):
- """Automatically set the name by replacing spaces with hyphens in the title."""
- self.name = self.title.replace(" ", "-")
+ def autoname(self):
+ """Automatically set the name by replacing spaces with hyphens in the title."""
+ self.name = self.title.replace(" ", "-")
+
+ def validate(self):
+ """Validate the Telegram API token and set the webhook if needed."""
+ self.validate_api_token()
+ self.set_webhook()
+
+ def validate_api_token(self):
+ """Validate the Telegram API token using Telegram's getMe API."""
+ if not self.has_value_changed("telegram_api_token"):
+ return # Skip validation if token hasn't changed
- def validate(self):
- """Validate the Telegram API token and set the webhook if needed."""
- self.validate_api_token()
- self.set_webhook()
+ frappe.db.commit() #Commit current changes to get recent decrypted token
+ api_token = self.get_password("telegram_api_token")
- def validate_api_token(self):
- """Validate the Telegram API token using Telegram's getMe API."""
- if not self.has_value_changed("telegram_api_token"):
- return # Skip validation if token hasn't changed
+ url = f"https://api.telegram.org/bot{api_token}/getMe"
- frappe.db.commit() #Commit current changes to get recent decrypted token
- api_token = self.get_password("telegram_api_token")
+ try:
+ response = requests.get(url)
+ response.raise_for_status() # Raises an HTTPError for 4xx/5xx status codes
- url = f"https://api.telegram.org/bot{api_token}/getMe"
+ data = response.json()
- try:
- response = requests.get(url)
- response.raise_for_status() # Raises an HTTPError for 4xx/5xx status codes
+ # Ensure that the API response contains valid bot information
+ if data.get("result", {}).get("is_bot"):
+ self.telegram_username = "@" + data["result"]["username"]
+ else:
+ frappe.throw("The Telegram user is not a bot.")
- data = response.json()
+ except requests.exceptions.RequestException as e:
+ frappe.throw(f"Failed to validate Telegram API token: {e}")
- # Ensure that the API response contains valid bot information
- if data.get("result", {}).get("is_bot"):
- self.telegram_username = "@" + data["result"]["username"]
- else:
- frappe.throw("The Telegram user is not a bot.")
+ def set_webhook(self):
+ """Set the Telegram webhook URL using Telegram's setWebhook API."""
+ if self.has_value_changed("telegram_webhook_url"):
+ api_token = self.get_password("telegram_api_token")
+ self.del_webhook()
+ s_token=self.get_secret_token()
+ webhook_url = f"{self.telegram_webhook_url}/api/method/chatbot.webhook.telegram_webhook"
+ url = f"https://api.telegram.org/bot{api_token}/setWebhook?url={webhook_url}&secret_token={s_token}&drop_pending_updates=True"
- except requests.exceptions.RequestException as e:
- frappe.throw(f"Failed to validate Telegram API token: {e}")
+ try:
- def set_webhook(self):
- """Set the Telegram webhook URL using Telegram's setWebhook API."""
- if self.has_value_changed("telegram_webhook_url"):
- api_token = self.get_password("telegram_api_token")
+ response = requests.get(url)
+ response.raise_for_status() # Raises an HTTPError for 4xx/5xx status codes
- webhook_url = f"{self.telegram_webhook_url}/api/method/chatbot.webhook.telegram_webhook"
- url = f"https://api.telegram.org/bot{api_token}/setWebhook?url={webhook_url}"
+ except requests.exceptions.RequestException as e:
+ frappe.throw(f"Failed to set Telegram webhook: {e}")
+ def del_webhook(self):
+ api_token = self.get_password("telegram_api_token")
+ url=f"https://api.telegram.org/bot{api_token}/deleteWebhook?drop_pending_updates=1"
+ try:
- try:
- response = requests.get(url)
- response.raise_for_status() # Raises an HTTPError for 4xx/5xx status codes
+ response = requests.get(url)
+ response.raise_for_status() # Raises an HTTPError for 4xx/5xx status codes
- except requests.exceptions.RequestException as e:
- frappe.throw(f"Failed to set Telegram webhook: {e}")
+ except requests.exceptions.RequestException as e:
+ frappe.throw(f"Failed to set Telegram webhook: {e}")
+ def get_secret_token(self):
+ random_value_to_send =secrets.token_hex(32)
+ random_value=hashlib.sha256(random_value_to_send.encode())
+ random_value=random_value.hexdigest()
+ self.secret_token=passlibctx.hash(random_value)
+ return random_value_to_send
diff --git a/chatbot/customization/after_migrate.py b/chatbot/customization/after_migrate.py
index e585a3b..efccaaa 100644
--- a/chatbot/customization/after_migrate.py
+++ b/chatbot/customization/after_migrate.py
@@ -1,4 +1,4 @@
def after_migrate():
- pass
\ No newline at end of file
+ pass
diff --git a/chatbot/hooks.py b/chatbot/hooks.py
index b4b8212..39bef44 100644
--- a/chatbot/hooks.py
+++ b/chatbot/hooks.py
@@ -243,4 +243,3 @@
# default_log_clearing_doctypes = {
# "Logging DocType Name": 30 # days to retain logs
# }
-
diff --git a/chatbot/modules.txt b/chatbot/modules.txt
index 9f2bfd9..037aec8 100644
--- a/chatbot/modules.txt
+++ b/chatbot/modules.txt
@@ -1 +1 @@
-Chatbot
\ No newline at end of file
+Chatbot
diff --git a/chatbot/patches.txt b/chatbot/patches.txt
index f15c3a9..73efa6f 100644
--- a/chatbot/patches.txt
+++ b/chatbot/patches.txt
@@ -3,4 +3,4 @@
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
[post_model_sync]
-# Patches added in this section will be executed after doctypes are migrated
\ No newline at end of file
+# Patches added in this section will be executed after doctypes are migrated
diff --git a/chatbot/utils.py b/chatbot/utils.py
index 491907a..68883eb 100644
--- a/chatbot/utils.py
+++ b/chatbot/utils.py
@@ -1,41 +1,76 @@
import frappe
-
+import os
+import pdfkit
+import json
+from datetime import datetime
+from frappe.www.printview import get_html_and_style
def validate_user(user_name:str, service_name:str):
- party_list = frappe.db.get_all("Chatbot Party Type", pluck="name")
+ party_list = frappe.db.get_all("Chatbot Party Type", pluck="name")
- if service_name == "Telegram":
- for party in party_list:
- party_name = frappe.db.get_value(party, {"telegram_username":user_name})
- if party_name:
- return party, party_name
+ if service_name == "Telegram":
+ for party in party_list:
+ party_name = frappe.db.get_value(party, {"telegram_username":user_name})
+ if party_name:
+ return party, party_name
- return None, None
+ return None, None
-def get_root_chatbot_flow():
- root_doc = frappe.db.get_value('Chatbot Flow',
- {'parent_chatbot_flow': ["is", "null"], 'button_text' : ["is", "null"]},
- ['template', 'name'], as_dict=1)
+def get_root_chatbot_flow(party_type):
+ party_tree=frappe.db.get_all("Chatbot Associated Party Types",{"parent":["is","set"],"parenttype":"Chatbot Flow","party_name": party_type},["parent"],pluck="parent")
+ root_doc = frappe.db.get_value('Chatbot Flow',
+ {'parent_chatbot_flow': ["is", "null"], 'button_text' : ["is", "null"],"name":["in",party_tree]},
+ ['template', 'name'], as_dict=1)
- if root_doc:
- return root_doc
- else:
- frappe.throw("No root document found for Chatbot Flow.", exc=frappe.DoesNotExistError)
+ if root_doc:
+ return root_doc
+ else:
+ frappe.throw("No root document found for Chatbot Flow.", exc=frappe.DoesNotExistError)
def get_associated_party_types(docname:str):
- party_types = frappe.db.get_all("Chatbot Associated Party Types",
- filters={'parent':docname},
- pluck='party_name'
- )
+ party_types = frappe.db.get_all("Chatbot Associated Party Types",
+ filters={'parent':docname},
+ pluck='party_name'
+ )
- return party_types
+ return party_types
def fetch_all_children(docname:str):
- children = frappe.db.get_all('Chatbot Flow',
- filters={'parent_chatbot_flow':docname},
- fields=['name', 'button_text']
- )
+ children = frappe.db.get_all('Chatbot Flow',
+ filters={'parent_chatbot_flow':docname},
+ fields=['name', 'button_text']
+ )
- return children
+ return children
+def send_document(base_html=None,name=None,options = {'orientation':'Portrait'},type = "pdf"):
+ if type == "pdf_value":
+ now = datetime.now()
+ now = now.strftime("%d-%m-%Y")
+ statement_id =str(name)+"_"+str(now)
+ path = os.getcwd()+frappe.utils.get_site_base_path()[1:]+"/public/files/"+statement_id+".pdf"
+ pdfkit.from_string(base_html, path,options={'orientation':'Portrait'})
+ return path
+def send_file_contet(path):
+ with open(path, 'rb') as file:
+ return file
+def get_prinformat_html(doctype,name):
+ base_html=get_html_and_style(doc=doctype,name=name)
+ base_html=""" """+base_html.get("style")+base_html.get("html")+" "
+ return base_html
+def get_report_html(report,filters=None):
+ base_html=""
+ if not filters:
+ filters= '{"company":"A (Demo)","from_date":"2024-08-27","to_date":"2024-09-27","group_by":"Group by Voucher (Consolidated)","include_dimensions":1,"show_opening_entries":0,"include_default_book_entries":1,"show_cancelled_entries":0,"show_net_values_in_party_account":0,"add_values_in_transaction_currency":0,"show_remarks":0,"ignore_err":0,"ignore_cr_dr_notes":0}'
+ if report == "General Ledger":
+ report_get_htm_doc=frappe.new_doc("Auto Email Report")
+ report_get_htm_doc.report=report
+ report_get_htm_doc.filters =filters
+ report_get_htm_doc.report_type="Script Report"
+ report_get_htm_doc.user="Administrator"
+ report_get_htm_doc.name="General Ledger"
+ report_get_htm_doc.description="General Ledger"
+ report_get_htm_doc.format = "HTML"
+ base_html=report_get_htm_doc.get_report_content()
+ return base_html
diff --git a/chatbot/webhook.py b/chatbot/webhook.py
index ed13390..c3d90dd 100644
--- a/chatbot/webhook.py
+++ b/chatbot/webhook.py
@@ -8,7 +8,8 @@ def telegram_webhook():
"""Webhook endpoint for Telegram."""
try:
update = frappe.request.get_json()
- api = TelegramAPI(update)
+ headers=frappe.request.headers
+ api = TelegramAPI(update,headers)
try:
api.process_update()
@@ -22,4 +23,4 @@ def telegram_webhook():
# Log the error and return an error response
frappe.log_error(title="Error processing webhook" ,message=frappe.get_traceback(with_context=1))
- return {"status": "error", "message": "Failed to process update"}, 500
\ No newline at end of file
+ return {"status": "error", "message": "Failed to process update"}, 500