From dbed5f08b3230d535a6c6a1d9f901cb6ee9f4837 Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:29:00 -0400 Subject: [PATCH 1/7] feat: Cohere telegra.ph - Enable Cohere web search - Output the entire LLM message with its source link on Telegraph --- README.md | 16 +++ handlers/__init__.py | 251 +++++++++++++++++++++++++++++++++++++++++++ handlers/cohere.py | 211 ++++++++++++++++++++++++++++++++++++ handlers/useful.py | 89 +++++++++++++++ 4 files changed, 567 insertions(+) create mode 100644 handlers/cohere.py diff --git a/README.md b/README.md index e682b09..790eb95 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,22 @@ Note, if you are using third party service, you need to `export OPENAI_API_BASE= Note, currently its support dify Chatbot with instructions(System prompt) and different MODEL with its parameters. +## Bot -> Cohere + +1. visit https://dashboard.cohere.com/api-keys get the key +2. export COHERE_API_KEY=${the_key} +3. use `cohere: ${message}` to ask + +## Bot -> `Telegra.ph` + +1. https://t.me/telegraph Create or login Telegraph account +2. `Log in as ${Account} on this device` +3. On Browser at https://telegra.ph/, press F12 or right click and inspect +4. Go to Application -> Storage -> Cookies -> https://telegra.ph/ +5. The token at `tph_token` is the token for telegra.ph API + +Do not share the token with others, it's like a password. + ## HOW TO Install and Run ### Manually install diff --git a/handlers/__init__.py b/handlers/__init__.py index 9a960ea..58074e3 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -200,10 +200,261 @@ def image_to_data_uri(file_path): return f"data:image/png;base64,{encoded_image}" +import requests +import json +import markdown # pip install Markdown +from bs4 import BeautifulSoup # pip install beautifulsoup4 + + +class TelegraphAPI: + def __init__(self, access_token): + self.access_token = access_token + self.base_url = "https://api.telegra.ph" + + # Get account info on initialization + account_info = self.get_account_info() + self.short_name = account_info.get("short_name") + self.author_name = account_info.get("author_name") + self.author_url = account_info.get("author_url") + + def create_page( + self, title, content, author_name=None, author_url=None, return_content=False + ): + """ + Creates a new Telegraph page. + + Args: + title (str): Page title (1-256 characters). + content (list): Content of the page as a list of Node dictionaries. + author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. + author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. + return_content (bool, optional): If True, return the content field in the response. + + Returns: + str: URL of the created page. + + Raises: + requests.exceptions.RequestException: If the request fails. + + + """ + url = f"{self.base_url}/createPage" + data = { + "access_token": self.access_token, + "title": title, + "content": json.dumps(content), + "return_content": return_content, + # Use provided author info or fall back to account info + "author_name": author_name if author_name else self.author_name, + "author_url": author_url if author_url else self.author_url, + } + + response = requests.post(url, data=data) + response.raise_for_status() + response = response.json() + page_url = response["result"]["url"] + return page_url + + def get_account_info(self): + """ + Gets information about the Telegraph account. + + Returns: + dict: Account information including short_name, author_name, and author_url. + Returns None if there's an error. + """ + url = f"{self.base_url}/getAccountInfo?access_token={self.access_token}" # &fields=[\"author_name\",\"author_url\"] for specific fields + response = requests.get(url) + + if response.status_code == 200: + return response.json()["result"] + else: + print(f"Fail getting telegra.ph token info: {response.status_code}") + return None + + def edit_page( + self, + path, + title, + content, + author_name=None, + author_url=None, + return_content=False, + ): + """ + Edits an existing Telegraph page. + + Args: + path (str): Path of the page to edit. + title (str): New page title (1-256 characters). + content (list): New content of the page as a list of Node dictionaries. + author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. + author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. + return_content (bool, optional): If True, return the content field in the response. + + Returns: + str: URL of the edited page. + + Raises: + requests.exceptions.RequestException: If the request fails. + """ + url = f"{self.base_url}/editPage" + data = { + "access_token": self.access_token, + "path": path, + "title": title, + "content": json.dumps(content), + "return_content": return_content, + # Use provided author info or fall back to account info + "author_name": author_name if author_name else self.author_name, + "author_url": author_url if author_url else self.author_url, + } + + response = requests.post(url, data=data) + response.raise_for_status() + response = response.json() + + page_url = response["result"]["url"] + return page_url + + def get_page(self, path): + """ + Gets information about a Telegraph page. + + Args: + path (str): Path of the page to get. + + Returns: + dict: Information about the page. + """ + url = f"{self.base_url}/getPage/{path}?return_content=true" + response = requests.get(url) + response.raise_for_status() + return response.json()["result"] + + def create_page_md( + self, + title, + markdown_text, + author_name=None, + author_url=None, + return_content=False, + ): + """ + Creates a new Telegraph page from markdown text. + + Args: + title (str): Page title (1-256 characters). + markdown_text (str): Markdown text to convert to HTML. + author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. + author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. + return_content (bool, optional): If True, return the content field in the response. + + Returns: + str: URL of the created page. + + Raises: + requests.exceptions.RequestException: If the request fails. + """ + content = md_to_dom(markdown_text) + return self.create_page(title, content, author_name, author_url, return_content) + + def edit_page_md( + self, + path, + title, + markdown_text, + author_name=None, + author_url=None, + return_content=False, + ): + content = md_to_dom(markdown_text) + return self.edit_page( + path, title, content, author_name, author_url, return_content + ) + + +def md_to_dom(markdown_text): + """Converts markdown text to a Python dictionary representing the DOM, + limiting heading levels to h3 and h4. + + Args: + markdown_text: The markdown text to convert. + + Returns: + A Python list representing the DOM, where each element is a dictionary + with the following keys: + - 'tag': The tag name of the element. + - 'attributes': A dictionary of attributes for the element (optional). + - 'children': A list of child elements (optional). + """ + + # Convert markdown to HTML + html = markdown.markdown( + markdown_text, + extensions=["markdown.extensions.extra", "markdown.extensions.sane_lists"], + ) + + # Parse the HTML with BeautifulSoup + soup = BeautifulSoup(html, "html.parser") + + def parse_element(element): + tag_dict = {"tag": element.name} + if element.name in ["h1", "h2", "h3", "h4", "h5", "h6"]: + if element.name == "h1": + tag_dict["tag"] = "h3" + elif element.name == "h2": + tag_dict["tag"] = "h4" + else: + tag_dict["tag"] = "p" + tag_dict["children"] = [{"tag": "strong", "children": element.contents}] + + # Correctly handle children for h1-h6 + if element.attrs: + tag_dict["attributes"] = element.attrs + if element.contents: + children = [] + for child in element.contents: + if isinstance(child, str): + # Remove leading/trailing whitespace from text nodes + children.append(child.strip()) + else: # it's another tag + children.append(parse_element(child)) + tag_dict["children"] = children + else: + if element.attrs: + tag_dict["attributes"] = element.attrs + if element.contents: + children = [] + for child in element.contents: + if isinstance(child, str): + # Remove leading/trailing whitespace from text nodes + children.append(child.strip()) + else: # it's another tag + children.append(parse_element(child)) + if children: + tag_dict["children"] = children + return tag_dict + + new_dom = [] + for element in soup.contents: + if isinstance(element, str) and not element.strip(): + # Skip empty text nodes + continue + elif isinstance(element, str): + # Treat remaining text nodes as separate elements for clarity + new_dom.append({"tag": "text", "content": element.strip()}) + else: + new_dom.append(parse_element(element)) + + return new_dom + + # `import *` will give you these __all__ = [ "bot_reply_first", "bot_reply_markdown", "enrich_text_with_urls", "image_to_data_uri", + "TelegraphAPI", ] diff --git a/handlers/cohere.py b/handlers/cohere.py new file mode 100644 index 0000000..69b5fc1 --- /dev/null +++ b/handlers/cohere.py @@ -0,0 +1,211 @@ +from os import environ +import time + +from telebot import TeleBot +from telebot.types import Message +from expiringdict import ExpiringDict + +from . import * + +import cohere +from telegramify_markdown import convert +from telegramify_markdown.customize import markdown_symbol + +markdown_symbol.head_level_1 = "📌" # If you want, Customizing the head level 1 symbol +markdown_symbol.link = "🔗" # If you want, Customizing the link symbol + +COHERE_API_KEY = environ.get("COHERE_API_KEY") +COHERE_MODEL = "command-r-plus" + +TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") +if TELEGRA_PH_TOKEN: + ph = TelegraphAPI(TELEGRA_PH_TOKEN) + +if COHERE_API_KEY: + co = cohere.Client(api_key=COHERE_API_KEY) + +# Global history cache +cohere_player_dict = ExpiringDict(max_len=1000, max_age_seconds=300) + + +def cohere_handler_direct(message: Message, bot: TeleBot) -> None: + """cohere : /cohere """ + m = message.text.strip() + + player_message = [] + if str(message.from_user.id) not in cohere_player_dict: + cohere_player_dict[str(message.from_user.id)] = player_message + else: + player_message = cohere_player_dict[str(message.from_user.id)] + + if m.strip() == "clear": + bot.reply_to( + message, + "Just cleared your Cohere messages history", + ) + player_message.clear() + return + + if m[:4].lower() == "new ": + m = m[4:].strip() + player_message.clear() + + m = enrich_text_with_urls(m) + + who = "Command R Plus" + reply_id = bot_reply_first(message, who, bot) + + player_message.append({"role": "User", "message": m}) + # keep the last 5, every has two ask and answer. + if len(player_message) > 10: + player_message = player_message[2:] + + try: + stream = co.chat_stream( + model=COHERE_MODEL, + message=m, + temperature=0.8, + chat_history=player_message, + prompt_truncation="AUTO", + connectors=[{"id": "web-search"}], + citation_quality="accurate", + ) + + s = "" + source = "" + start = time.time() + for event in stream: + if event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 1.2: + start = time.time() + bot_reply_markdown(reply_id, who, s, bot, split_text=True) + elif event.event_type == "search-results": + for doc in event.documents: + source += f"\n[{doc['title']}]({doc['url']})" + elif event.event_type == "stream-end": + break + + s += "\n" + source + "\n" + + if not bot_reply_markdown(reply_id, who, s, bot): + # maybe not complete + # maybe the same message + player_message.clear() + return + + player_message.append( + { + "role": "Chatbot", + "message": convert(s), + } + ) + + except Exception as e: + print(e) + bot_reply_markdown(reply_id, who, "Answer wrong", bot) + player_message.clear() + return + + +def cohere_handler(message: Message, bot: TeleBot) -> None: + """cohere : /cohere """ + m = message.text.strip() + + player_message = [] + if str(message.from_user.id) not in cohere_player_dict: + cohere_player_dict[str(message.from_user.id)] = player_message + else: + player_message = cohere_player_dict[str(message.from_user.id)] + + if m.strip() == "clear": + bot.reply_to( + message, + "Just cleared your Cohere messages history", + ) + player_message.clear() + return + + if m[:4].lower() == "new ": + m = m[4:].strip() + player_message.clear() + + m = enrich_text_with_urls(m) + + who = "Command R Plus" + reply_id = bot_reply_first(message, who, bot) + + player_message.append({"role": "User", "message": m}) + # keep the last 5, every has two ask and answer. + if len(player_message) > 10: + player_message = player_message[2:] + + try: + stream = co.chat_stream( + model=COHERE_MODEL, + message=m, + temperature=0.8, + chat_history=player_message, + prompt_truncation="AUTO", + connectors=[{"id": "web-search"}], + citation_quality="accurate", + ) + + s = "" + source = "" + start = time.time() + for event in stream: + if event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 1.2: + start = time.time() + bot_reply_markdown(reply_id, who, s, bot, split_text=False) + elif event.event_type == "search-results": + for doc in event.documents: + source += f"\n{doc['title']}\n{doc['url']}\n" + elif event.event_type == "stream-end": + break + content = s + "\n------\n------\n" + source + ph_s = ph.create_page_md(title="Cohere", markdown_text=content) + s += f"\n\n[View]({ph_s})" + + if not bot_reply_markdown(reply_id, who, s, bot): + # maybe not complete + # maybe the same message + player_message.clear() + return + + player_message.append( + { + "role": "Chatbot", + "message": convert(s), + } + ) + + except Exception as e: + print(e) + bot_reply_markdown(reply_id, who, "Answer wrong", bot) + player_message.clear() + return + + +if COHERE_API_KEY: + if not TELEGRA_PH_TOKEN: + + def register(bot: TeleBot) -> None: + bot.register_message_handler( + cohere_handler_direct, commands=["cohere"], pass_bot=True + ) + bot.register_message_handler( + cohere_handler_direct, regexp="^cohere:", pass_bot=True + ) + + else: + + def register(bot: TeleBot) -> None: + bot.register_message_handler( + cohere_handler_direct, commands=["cohere"], pass_bot=True + ) + bot.register_message_handler( + cohere_handler_direct, regexp="^cohere:", pass_bot=True + ) diff --git a/handlers/useful.py b/handlers/useful.py index 686f000..0416502 100644 --- a/handlers/useful.py +++ b/handlers/useful.py @@ -18,6 +18,14 @@ from . import * from telegramify_markdown.customize import markdown_symbol +import cohere + +COHERE_API_KEY = environ.get("COHERE_API_KEY") +TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") +co = cohere.Client(api_key=COHERE_API_KEY) +ph = TelegraphAPI(TELEGRA_PH_TOKEN) +COHERE_MODEL = "command-r-plus" + chat_message_dict = ExpiringDict(max_len=100, max_age_seconds=120) chat_user_dict = ExpiringDict(max_len=100, max_age_seconds=20) @@ -91,6 +99,7 @@ def latest_handle_messages(message: Message, bot: TeleBot): "sd", "map", "yi", + "cohere", ) ): return @@ -120,6 +129,7 @@ def answer_it_handler(message: Message, bot: TeleBot): latest_message = chat_message_dict.get(chat_id) m = latest_message.text.strip() m = enrich_text_with_urls(m) + full = "" ##### Gemini ##### who = "Gemini Pro" # show something, make it more responsible @@ -141,6 +151,8 @@ def answer_it_handler(message: Message, bot: TeleBot): convo.history.clear() bot_reply_markdown(reply_id, who, "Error", bot) + full += f"{who}:\n{s}" + chat_id_list = [reply_id.message_id] ##### ChatGPT ##### who = "ChatGPT Pro" reply_id = bot_reply_first(latest_message, who, bot) @@ -173,6 +185,83 @@ def answer_it_handler(message: Message, bot: TeleBot): print(e) bot_reply_markdown(reply_id, who, "answer wrong", bot) + full += f"\n---\n{who}:\n{s}" + chat_id_list.append(reply_id.message_id) + + ##### Cohere ##### + if COHERE_API_KEY: + full, chat_id = cohere_answer(latest_message, bot, full, m) + chat_id_list.append(chat_id) + else: + pass + + ##### Answer ##### + if TELEGRA_PH_TOKEN: + final_answer(latest_message, bot, full, chat_id_list) + else: + pass + + +def cohere_answer(latest_message: Message, bot: TeleBot, full, m): + """cohere answer""" + who = "Command R Plus" + reply_id = bot_reply_first(latest_message, who, bot) + + player_message = [{"role": "User", "message": m}] + + try: + r = co.chat_stream( + model=COHERE_MODEL, + message=m, + temperature=0.4, + chat_history=player_message, + prompt_truncation="AUTO", + connectors=[{"id": "web-search"}], + citation_quality="fast", + ) + s = "" + source = "" + start = time.time() + for event in r: + if event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 1.2: + start = time.time() + bot_reply_markdown(reply_id, who, s, bot, split_text=False) + elif event.event_type == "search-results": + for doc in event.documents: + source += f"\n[{doc['title']}]({doc['url']})" + elif event.event_type == "stream-end": + break + + # maybe not complete + # maybe the same message + try: + bot_reply_markdown(reply_id, who, s, bot) + except: + pass + + except Exception as e: + print(e) + bot_reply_markdown(reply_id, who, "Answer wrong", bot) + + content = s + "\n------\n" + source + full += f"\n---\n{who}:\n{content}" + chat_id = reply_id.chat.id + return full, chat_id + + +def final_answer(latest_message: Message, bot: TeleBot, full, list): + """final answer""" + who = "Answer" + reply_id = bot_reply_first(latest_message, who, bot) + ph_s = ph.create_page_md(title="Answer it", markdown_text=full) + bot_reply_markdown(reply_id, who, f"[View]({ph_s})", bot) + # delete the chat message, only leave a telegra.ph link + + # for i in list: + # bot.delete_message(chat_id=chat_id, message_id=i) + if GOOGLE_GEMINI_KEY and CHATGPT_API_KEY: From 3913c5e7a82379a2841316ed362892a0f333bb9d Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:40:42 -0400 Subject: [PATCH 2/7] chore: update --- handlers/cohere.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handlers/cohere.py b/handlers/cohere.py index 69b5fc1..5face73 100644 --- a/handlers/cohere.py +++ b/handlers/cohere.py @@ -204,8 +204,8 @@ if COHERE_API_KEY: def register(bot: TeleBot) -> None: bot.register_message_handler( - cohere_handler_direct, commands=["cohere"], pass_bot=True + cohere_handler, commands=["cohere"], pass_bot=True ) bot.register_message_handler( - cohere_handler_direct, regexp="^cohere:", pass_bot=True + cohere_handler, regexp="^cohere:", pass_bot=True ) From d104201b00ccf76fddc32c07ebb0e972137430a7 Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Sun, 23 Jun 2024 02:20:53 -0400 Subject: [PATCH 3/7] fix: Long message handle and decorate waiting message --- handlers/cohere.py | 58 +++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/handlers/cohere.py b/handlers/cohere.py index 5face73..1051e99 100644 --- a/handlers/cohere.py +++ b/handlers/cohere.py @@ -1,5 +1,6 @@ from os import environ import time +import datetime from telebot import TeleBot from telebot.types import Message @@ -69,30 +70,34 @@ def cohere_handler_direct(message: Message, bot: TeleBot) -> None: prompt_truncation="AUTO", connectors=[{"id": "web-search"}], citation_quality="accurate", + preamble=f"You are Command R+, a large language model trained to have polite, helpful, inclusive conversations with people. The current time in Tornoto is {datetime.datetime.now(datetime.timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M:%S')}, in Los Angeles is {datetime.datetime.now(datetime.timezone.utc).astimezone().astimezone(datetime.timezone(datetime.timedelta(hours=-7))).strftime('%Y-%m-%d %H:%M:%S')}, and in China is {datetime.datetime.now(datetime.timezone.utc).astimezone(datetime.timezone(datetime.timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S')}.", ) s = "" source = "" start = time.time() for event in stream: - if event.event_type == "text-generation": - s += event.text.encode("utf-8").decode("utf-8") - if time.time() - start > 1.2: - start = time.time() - bot_reply_markdown(reply_id, who, s, bot, split_text=True) + if event.event_type == "stream-start": + bot_reply_markdown(reply_id, who, "Thinking...", bot) + elif event.event_type == "search-queries-generation": + bot_reply_markdown(reply_id, who, "Searching online...", bot) elif event.event_type == "search-results": + bot_reply_markdown(reply_id, who, "Reading...", bot) for doc in event.documents: source += f"\n[{doc['title']}]({doc['url']})" + elif event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 0.4: + start = time.time() + bot_reply_markdown(reply_id, who, f"\nStill thinking{len(s)}...", bot, split_text=True) elif event.event_type == "stream-end": break - s += "\n" + source + "\n" - if not bot_reply_markdown(reply_id, who, s, bot): - # maybe not complete - # maybe the same message - player_message.clear() - return + try: + bot_reply_markdown(reply_id, who, s, bot, split_text=True) + except: + pass player_message.append( { @@ -109,7 +114,7 @@ def cohere_handler_direct(message: Message, bot: TeleBot) -> None: def cohere_handler(message: Message, bot: TeleBot) -> None: - """cohere : /cohere """ + """cohere : /cohere This will return a telegraph link""" m = message.text.strip() player_message = [] @@ -149,31 +154,36 @@ def cohere_handler(message: Message, bot: TeleBot) -> None: prompt_truncation="AUTO", connectors=[{"id": "web-search"}], citation_quality="accurate", + preamble=f"You are Command R+, a large language model trained to have polite, helpful, inclusive conversations with people. The current time in Tornoto is {datetime.datetime.now(datetime.timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M:%S')}, in Los Angeles is {datetime.datetime.now(datetime.timezone.utc).astimezone().astimezone(datetime.timezone(datetime.timedelta(hours=-7))).strftime('%Y-%m-%d %H:%M:%S')}, and in China is {datetime.datetime.now(datetime.timezone.utc).astimezone(datetime.timezone(datetime.timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S')}.", ) s = "" source = "" start = time.time() for event in stream: - if event.event_type == "text-generation": - s += event.text.encode("utf-8").decode("utf-8") - if time.time() - start > 1.2: - start = time.time() - bot_reply_markdown(reply_id, who, s, bot, split_text=False) + if event.event_type == "stream-start": + bot_reply_markdown(reply_id, who, "Thinking...", bot) + elif event.event_type == "search-queries-generation": + bot_reply_markdown(reply_id, who, "Searching online...", bot) elif event.event_type == "search-results": + bot_reply_markdown(reply_id, who, "Reading...", bot) for doc in event.documents: source += f"\n{doc['title']}\n{doc['url']}\n" + elif event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 0.4: + start = time.time() + bot_reply_markdown(reply_id, who, f"\nStill thinking{len(s)}...", bot, split_text=True) elif event.event_type == "stream-end": break - content = s + "\n------\n------\n" + source - ph_s = ph.create_page_md(title="Cohere", markdown_text=content) + content = s + "\n------\n------\n" + source + f"\n------\n------\nLast Update{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + ph_s = ph.create_page_md(title="Cohere", markdown_text=content) # or edit_page with get_page so not producing massive pages s += f"\n\n[View]({ph_s})" - if not bot_reply_markdown(reply_id, who, s, bot): - # maybe not complete - # maybe the same message - player_message.clear() - return + try: + bot_reply_markdown(reply_id, who, s, bot, split_text=True) + except: + pass player_message.append( { From bfad22ffaf1db91e1e7ec2393e6e022d19076648 Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Sun, 23 Jun 2024 02:43:02 -0400 Subject: [PATCH 4/7] chore: black format --- handlers/cohere.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/handlers/cohere.py b/handlers/cohere.py index 1051e99..aabdc6a 100644 --- a/handlers/cohere.py +++ b/handlers/cohere.py @@ -89,7 +89,13 @@ def cohere_handler_direct(message: Message, bot: TeleBot) -> None: s += event.text.encode("utf-8").decode("utf-8") if time.time() - start > 0.4: start = time.time() - bot_reply_markdown(reply_id, who, f"\nStill thinking{len(s)}...", bot, split_text=True) + bot_reply_markdown( + reply_id, + who, + f"\nStill thinking{len(s)}...", + bot, + split_text=True, + ) elif event.event_type == "stream-end": break s += "\n" + source + "\n" @@ -173,11 +179,24 @@ def cohere_handler(message: Message, bot: TeleBot) -> None: s += event.text.encode("utf-8").decode("utf-8") if time.time() - start > 0.4: start = time.time() - bot_reply_markdown(reply_id, who, f"\nStill thinking{len(s)}...", bot, split_text=True) + bot_reply_markdown( + reply_id, + who, + f"\nStill thinking{len(s)}...", + bot, + split_text=True, + ) elif event.event_type == "stream-end": break - content = s + "\n------\n------\n" + source + f"\n------\n------\nLast Update{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - ph_s = ph.create_page_md(title="Cohere", markdown_text=content) # or edit_page with get_page so not producing massive pages + content = ( + s + + "\n------\n------\n" + + source + + f"\n------\n------\nLast Update{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + ) + ph_s = ph.create_page_md( + title="Cohere", markdown_text=content + ) # or edit_page with get_page so not producing massive pages s += f"\n\n[View]({ph_s})" try: From e2ffd7fa570ea5152397c81e1757cc8f30d2c58b Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Sun, 23 Jun 2024 09:14:19 -0400 Subject: [PATCH 5/7] feat: create telegraph account - feat: create and store the telegraph token locally - check and use if there exist token in json - feat: auto generate telegraph token for cohere.py and answer_it - fix: optional delete other answer_it message and only leave a telegraph link messsage for clean view Co-Authored-By: yihong --- .gitignore | 3 +- handlers/__init__.py | 52 ++++++++++++++++++++++++++++++++ handlers/cohere.py | 32 ++++++++++---------- handlers/useful.py | 71 ++++++++++++++++++++++++++++---------------- 4 files changed, 114 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 77d4e40..b468ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -168,4 +168,5 @@ nohup.out *.mp4 *.pdf .pdm-python -*.wav \ No newline at end of file +*.wav +telegraph_token.json diff --git a/handlers/__init__.py b/handlers/__init__.py index 58074e3..0788c4b 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -206,6 +206,57 @@ import markdown # pip install Markdown from bs4 import BeautifulSoup # pip install beautifulsoup4 +def create_ph_account(short_name: str, author_name: str, author_url: str = None) -> str: + """ + Creates a new account on the Telegra.ph platform. + If an account already exists (stored in a local JSON file), returns the existing access token. + Otherwise, creates a new account and stores the information locally. + Sample request + https://api.telegra.ph/editAccountInfo?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722&short_name=Sandbox&author_name=Anonymous + + Args: + short_name (str): The short name of the account. + author_name (str): The name of the author. + author_url (str, optional): The URL of the author's profile. Defaults to None. + + Returns: + str: The access token for the account. + + Raises: + requests.RequestException: If the API request fails. + json.JSONDecodeError: If the API response is not valid JSON. + KeyError: If the API response does not contain the expected data. + """ + TELEGRAPH_API_URL = "https://api.telegra.ph/createAccount" + + # Try to load existing account information + try: + with open("telegraph_token.json", "r") as f: + account = json.load(f) + return account["result"]["access_token"] + except FileNotFoundError: + # If no existing account, create a new one + data = { + "short_name": short_name, + "author_name": author_name, + } + if author_url: + data["author_url"] = author_url + + # Make API request + response = requests.post(TELEGRAPH_API_URL, data=data) + response.raise_for_status() # Raises an HTTPError for bad responses + + account = response.json() + access_token = account["result"]["access_token"] + + # Store the new account information + with open("telegraph_token.json", "w") as f: + json.dump(account, f) + + return access_token + + class TelegraphAPI: def __init__(self, access_token): self.access_token = access_token @@ -457,4 +508,5 @@ __all__ = [ "enrich_text_with_urls", "image_to_data_uri", "TelegraphAPI", + "create_ph_account", ] diff --git a/handlers/cohere.py b/handlers/cohere.py index aabdc6a..b437fca 100644 --- a/handlers/cohere.py +++ b/handlers/cohere.py @@ -21,6 +21,11 @@ COHERE_MODEL = "command-r-plus" TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") if TELEGRA_PH_TOKEN: ph = TelegraphAPI(TELEGRA_PH_TOKEN) +else: + TELEGRA_PH_TOKEN = create_ph_account( + short_name="cohere", author_name="A Telegram Bot" + ) + ph = TelegraphAPI(TELEGRA_PH_TOKEN) if COHERE_API_KEY: co = cohere.Client(api_key=COHERE_API_KEY) @@ -219,22 +224,15 @@ def cohere_handler(message: Message, bot: TeleBot) -> None: if COHERE_API_KEY: - if not TELEGRA_PH_TOKEN: - def register(bot: TeleBot) -> None: - bot.register_message_handler( - cohere_handler_direct, commands=["cohere"], pass_bot=True - ) - bot.register_message_handler( - cohere_handler_direct, regexp="^cohere:", pass_bot=True - ) + def register(bot: TeleBot) -> None: + bot.register_message_handler( + cohere_handler_direct, commands=["cohere_no_ph"], pass_bot=True + ) + bot.register_message_handler( + cohere_handler_direct, regexp="^cohere_no_ph:", pass_bot=True + ) - else: - - def register(bot: TeleBot) -> None: - bot.register_message_handler( - cohere_handler, commands=["cohere"], pass_bot=True - ) - bot.register_message_handler( - cohere_handler, regexp="^cohere:", pass_bot=True - ) + def register(bot: TeleBot) -> None: + bot.register_message_handler(cohere_handler, commands=["cohere"], pass_bot=True) + bot.register_message_handler(cohere_handler, regexp="^cohere:", pass_bot=True) diff --git a/handlers/useful.py b/handlers/useful.py index 0416502..835adab 100644 --- a/handlers/useful.py +++ b/handlers/useful.py @@ -5,6 +5,7 @@ from telebot.types import Message from expiringdict import ExpiringDict from os import environ import time +import datetime from openai import OpenAI import google.generativeai as genai @@ -22,8 +23,15 @@ import cohere COHERE_API_KEY = environ.get("COHERE_API_KEY") TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") -co = cohere.Client(api_key=COHERE_API_KEY) -ph = TelegraphAPI(TELEGRA_PH_TOKEN) +if COHERE_API_KEY: + co = cohere.Client(api_key=COHERE_API_KEY) +if TELEGRA_PH_TOKEN: + ph = TelegraphAPI(TELEGRA_PH_TOKEN) +else: + TELEGRA_PH_TOKEN = create_ph_account( + short_name="Answer it", author_name="A Telegram Bot" + ) + ph = TelegraphAPI(TELEGRA_PH_TOKEN) COHERE_MODEL = "command-r-plus" chat_message_dict = ExpiringDict(max_len=100, max_age_seconds=120) @@ -196,10 +204,7 @@ def answer_it_handler(message: Message, bot: TeleBot): pass ##### Answer ##### - if TELEGRA_PH_TOKEN: - final_answer(latest_message, bot, full, chat_id_list) - else: - pass + final_answer(latest_message, bot, full, chat_id_list) def cohere_answer(latest_message: Message, bot: TeleBot, full, m): @@ -210,45 +215,60 @@ def cohere_answer(latest_message: Message, bot: TeleBot, full, m): player_message = [{"role": "User", "message": m}] try: - r = co.chat_stream( + stream = co.chat_stream( model=COHERE_MODEL, message=m, - temperature=0.4, + temperature=0.3, chat_history=player_message, prompt_truncation="AUTO", connectors=[{"id": "web-search"}], - citation_quality="fast", + citation_quality="accurate", + preamble=f"You are Command R+, a large language model trained to have polite, helpful, inclusive conversations with people. The current time in Tornoto is {datetime.datetime.now(datetime.timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M:%S')}, in Los Angeles is {datetime.datetime.now(datetime.timezone.utc).astimezone().astimezone(datetime.timezone(datetime.timedelta(hours=-7))).strftime('%Y-%m-%d %H:%M:%S')}, and in China is {datetime.datetime.now(datetime.timezone.utc).astimezone(datetime.timezone(datetime.timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S')}.", ) + s = "" source = "" start = time.time() - for event in r: - if event.event_type == "text-generation": - s += event.text.encode("utf-8").decode("utf-8") - if time.time() - start > 1.2: - start = time.time() - bot_reply_markdown(reply_id, who, s, bot, split_text=False) + for event in stream: + if event.event_type == "stream-start": + bot_reply_markdown(reply_id, who, "Thinking...", bot) + elif event.event_type == "search-queries-generation": + bot_reply_markdown(reply_id, who, "Searching online...", bot) elif event.event_type == "search-results": + bot_reply_markdown(reply_id, who, "Reading...", bot) for doc in event.documents: - source += f"\n[{doc['title']}]({doc['url']})" + source += f"\n{doc['title']}\n{doc['url']}\n" + elif event.event_type == "text-generation": + s += event.text.encode("utf-8").decode("utf-8") + if time.time() - start > 0.4: + start = time.time() + bot_reply_markdown( + reply_id, + who, + f"\nStill thinking{len(s)}...", + bot, + split_text=True, + ) elif event.event_type == "stream-end": break + content = ( + s + + "\n------\n------\n" + + source + + f"\n------\n------\nLast Update{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + ) - # maybe not complete - # maybe the same message try: - bot_reply_markdown(reply_id, who, s, bot) + bot_reply_markdown(reply_id, who, s, bot, split_text=True) except: pass - except Exception as e: print(e) bot_reply_markdown(reply_id, who, "Answer wrong", bot) - - content = s + "\n------\n" + source + player_message.clear() + return full, reply_id.message_id full += f"\n---\n{who}:\n{content}" - chat_id = reply_id.chat.id - return full, chat_id + return full, reply_id.message_id def final_answer(latest_message: Message, bot: TeleBot, full, list): @@ -258,9 +278,8 @@ def final_answer(latest_message: Message, bot: TeleBot, full, list): ph_s = ph.create_page_md(title="Answer it", markdown_text=full) bot_reply_markdown(reply_id, who, f"[View]({ph_s})", bot) # delete the chat message, only leave a telegra.ph link - # for i in list: - # bot.delete_message(chat_id=chat_id, message_id=i) + # bot.delete_message(latest_message.chat.id, i) if GOOGLE_GEMINI_KEY and CHATGPT_API_KEY: From 9c8638279e84527f03aed237faea99da8c7094c4 Mon Sep 17 00:00:00 2001 From: Alter-xyz <88554920+alterxyz@users.noreply.github.com> Date: Mon, 24 Jun 2024 21:26:44 -0400 Subject: [PATCH 6/7] feat: Default skip telegraph account It will print out the token in local terminal by default. If Store_Token = True, it will store token in "token_key.json" View my lite ver and try at --- .gitignore | 2 +- README.md | 14 +- handlers/__init__.py | 313 ++++++++++++++++--------------------------- handlers/cohere.py | 13 +- handlers/useful.py | 18 ++- 5 files changed, 141 insertions(+), 219 deletions(-) diff --git a/.gitignore b/.gitignore index b468ea3..d4d2196 100644 --- a/.gitignore +++ b/.gitignore @@ -169,4 +169,4 @@ nohup.out *.pdf .pdm-python *.wav -telegraph_token.json +token_key.json diff --git a/README.md b/README.md index 790eb95..2eced2d 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,19 @@ Note, currently its support dify Chatbot with instructions(System prompt) and di 2. export COHERE_API_KEY=${the_key} 3. use `cohere: ${message}` to ask -## Bot -> `Telegra.ph` +## Function -> Telegraph + +### Skip token (default) + +You do not need to do anything. + +But you may not be able to edit any generated post since you do not have the token. + +### Store token (recommended) + +Change "Store_Token" to "True" in "handlers/__init__.py" TelegraphAPI/_create_ph_account. It will store the token in "token_key.json". + +### Get token manually from Telegram account 1. https://t.me/telegraph Create or login Telegraph account 2. `Log in as ${Account} on this device` diff --git a/handlers/__init__.py b/handlers/__init__.py index 0788c4b..f2867cc 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -200,66 +200,26 @@ def image_to_data_uri(file_path): return f"data:image/png;base64,{encoded_image}" -import requests import json -import markdown # pip install Markdown -from bs4 import BeautifulSoup # pip install beautifulsoup4 - - -def create_ph_account(short_name: str, author_name: str, author_url: str = None) -> str: - """ - Creates a new account on the Telegra.ph platform. - If an account already exists (stored in a local JSON file), returns the existing access token. - Otherwise, creates a new account and stores the information locally. - Sample request - https://api.telegra.ph/editAccountInfo?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722&short_name=Sandbox&author_name=Anonymous - - Args: - short_name (str): The short name of the account. - author_name (str): The name of the author. - author_url (str, optional): The URL of the author's profile. Defaults to None. - - Returns: - str: The access token for the account. - - Raises: - requests.RequestException: If the API request fails. - json.JSONDecodeError: If the API response is not valid JSON. - KeyError: If the API response does not contain the expected data. - """ - TELEGRAPH_API_URL = "https://api.telegra.ph/createAccount" - - # Try to load existing account information - try: - with open("telegraph_token.json", "r") as f: - account = json.load(f) - return account["result"]["access_token"] - except FileNotFoundError: - # If no existing account, create a new one - data = { - "short_name": short_name, - "author_name": author_name, - } - if author_url: - data["author_url"] = author_url - - # Make API request - response = requests.post(TELEGRAPH_API_URL, data=data) - response.raise_for_status() # Raises an HTTPError for bad responses - - account = response.json() - access_token = account["result"]["access_token"] - - # Store the new account information - with open("telegraph_token.json", "w") as f: - json.dump(account, f) - - return access_token +import requests +import os +from bs4 import BeautifulSoup +import markdown class TelegraphAPI: - def __init__(self, access_token): - self.access_token = access_token + def __init__( + self, + access_token=None, + short_name="tg_bot_collections", + author_name="Telegram Bot Collections", + author_url=None, + ): + self.access_token = ( + access_token + if access_token + else self._create_ph_account(short_name, author_name, author_url) + ) self.base_url = "https://api.telegra.ph" # Get account info on initialization @@ -268,34 +228,58 @@ class TelegraphAPI: self.author_name = account_info.get("author_name") self.author_url = account_info.get("author_url") + def _create_ph_account(self, short_name, author_name, author_url): + Store_Token = False + TELEGRAPH_API_URL = "https://api.telegra.ph/createAccount" + TOKEN_FILE = "token_key.json" + + # Try to load existing token information + try: + with open(TOKEN_FILE, "r") as f: + tokens = json.load(f) + if "TELEGRA_PH_TOKEN" in tokens and tokens["TELEGRA_PH_TOKEN"] != "example": + return tokens["TELEGRA_PH_TOKEN"] + except FileNotFoundError: + tokens = {} + + # If no existing valid token in TOKEN_FILE, create a new account + data = { + "short_name": short_name, + "author_name": author_name, + "author_url": author_url, + } + + # Make API request + response = requests.post(TELEGRAPH_API_URL, data=data) + response.raise_for_status() + + account = response.json() + access_token = account["result"]["access_token"] + + # Update the token in the dictionary + tokens["TELEGRA_PH_TOKEN"] = access_token + + # Store the updated tokens + if Store_Token: + with open(TOKEN_FILE, "w") as f: + json.dump(tokens, f, indent=4) + else: + print(f"Token not stored to file, but here is your token:\n{access_token}") + + # Store it to the environment variable + os.environ["TELEGRA_PH_TOKEN"] = access_token + + return access_token + def create_page( self, title, content, author_name=None, author_url=None, return_content=False ): - """ - Creates a new Telegraph page. - - Args: - title (str): Page title (1-256 characters). - content (list): Content of the page as a list of Node dictionaries. - author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. - author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. - return_content (bool, optional): If True, return the content field in the response. - - Returns: - str: URL of the created page. - - Raises: - requests.exceptions.RequestException: If the request fails. - - - """ url = f"{self.base_url}/createPage" data = { "access_token": self.access_token, "title": title, "content": json.dumps(content), "return_content": return_content, - # Use provided author info or fall back to account info "author_name": author_name if author_name else self.author_name, "author_url": author_url if author_url else self.author_url, } @@ -307,14 +291,7 @@ class TelegraphAPI: return page_url def get_account_info(self): - """ - Gets information about the Telegraph account. - - Returns: - dict: Account information including short_name, author_name, and author_url. - Returns None if there's an error. - """ - url = f"{self.base_url}/getAccountInfo?access_token={self.access_token}" # &fields=[\"author_name\",\"author_url\"] for specific fields + url = f'{self.base_url}/getAccountInfo?access_token={self.access_token}&fields=["short_name","author_name","author_url","auth_url"]' response = requests.get(url) if response.status_code == 200: @@ -332,23 +309,6 @@ class TelegraphAPI: author_url=None, return_content=False, ): - """ - Edits an existing Telegraph page. - - Args: - path (str): Path of the page to edit. - title (str): New page title (1-256 characters). - content (list): New content of the page as a list of Node dictionaries. - author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. - author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. - return_content (bool, optional): If True, return the content field in the response. - - Returns: - str: URL of the edited page. - - Raises: - requests.exceptions.RequestException: If the request fails. - """ url = f"{self.base_url}/editPage" data = { "access_token": self.access_token, @@ -356,7 +316,6 @@ class TelegraphAPI: "title": title, "content": json.dumps(content), "return_content": return_content, - # Use provided author info or fall back to account info "author_name": author_name if author_name else self.author_name, "author_url": author_url if author_url else self.author_url, } @@ -369,15 +328,6 @@ class TelegraphAPI: return page_url def get_page(self, path): - """ - Gets information about a Telegraph page. - - Args: - path (str): Path of the page to get. - - Returns: - dict: Information about the page. - """ url = f"{self.base_url}/getPage/{path}?return_content=true" response = requests.get(url) response.raise_for_status() @@ -391,23 +341,7 @@ class TelegraphAPI: author_url=None, return_content=False, ): - """ - Creates a new Telegraph page from markdown text. - - Args: - title (str): Page title (1-256 characters). - markdown_text (str): Markdown text to convert to HTML. - author_name (str, optional): Author name (0-128 characters). Defaults to account's author_name. - author_url (str, optional): Profile link (0-512 characters). Defaults to account's author_url. - return_content (bool, optional): If True, return the content field in the response. - - Returns: - str: URL of the created page. - - Raises: - requests.exceptions.RequestException: If the request fails. - """ - content = md_to_dom(markdown_text) + content = self._md_to_dom(markdown_text) return self.create_page(title, content, author_name, author_url, return_content) def edit_page_md( @@ -419,86 +353,72 @@ class TelegraphAPI: author_url=None, return_content=False, ): - content = md_to_dom(markdown_text) + content = self._md_to_dom(markdown_text) return self.edit_page( path, title, content, author_name, author_url, return_content ) + def authorize_browser(self): + url = f'{self.base_url}/getAccountInfo?access_token={self.access_token}&fields=["auth_url"]' + response = requests.get(url) + response.raise_for_status() + return response.json()["result"]["auth_url"] -def md_to_dom(markdown_text): - """Converts markdown text to a Python dictionary representing the DOM, - limiting heading levels to h3 and h4. + def _md_to_dom(self, markdown_text): + html = markdown.markdown( + markdown_text, + extensions=["markdown.extensions.extra", "markdown.extensions.sane_lists"], + ) - Args: - markdown_text: The markdown text to convert. + soup = BeautifulSoup(html, "html.parser") - Returns: - A Python list representing the DOM, where each element is a dictionary - with the following keys: - - 'tag': The tag name of the element. - - 'attributes': A dictionary of attributes for the element (optional). - - 'children': A list of child elements (optional). - """ + def parse_element(element): + tag_dict = {"tag": element.name} + if element.name in ["h1", "h2", "h3", "h4", "h5", "h6"]: + if element.name == "h1": + tag_dict["tag"] = "h3" + elif element.name == "h2": + tag_dict["tag"] = "h4" + else: + tag_dict["tag"] = "p" + tag_dict["children"] = [ + {"tag": "strong", "children": element.contents} + ] - # Convert markdown to HTML - html = markdown.markdown( - markdown_text, - extensions=["markdown.extensions.extra", "markdown.extensions.sane_lists"], - ) - - # Parse the HTML with BeautifulSoup - soup = BeautifulSoup(html, "html.parser") - - def parse_element(element): - tag_dict = {"tag": element.name} - if element.name in ["h1", "h2", "h3", "h4", "h5", "h6"]: - if element.name == "h1": - tag_dict["tag"] = "h3" - elif element.name == "h2": - tag_dict["tag"] = "h4" - else: - tag_dict["tag"] = "p" - tag_dict["children"] = [{"tag": "strong", "children": element.contents}] - - # Correctly handle children for h1-h6 - if element.attrs: - tag_dict["attributes"] = element.attrs - if element.contents: - children = [] - for child in element.contents: - if isinstance(child, str): - # Remove leading/trailing whitespace from text nodes - children.append(child.strip()) - else: # it's another tag - children.append(parse_element(child)) - tag_dict["children"] = children - else: - if element.attrs: - tag_dict["attributes"] = element.attrs - if element.contents: - children = [] - for child in element.contents: - if isinstance(child, str): - # Remove leading/trailing whitespace from text nodes - children.append(child.strip()) - else: # it's another tag - children.append(parse_element(child)) - if children: + if element.attrs: + tag_dict["attributes"] = element.attrs + if element.contents: + children = [] + for child in element.contents: + if isinstance(child, str): + children.append(child.strip()) + else: + children.append(parse_element(child)) tag_dict["children"] = children - return tag_dict + else: + if element.attrs: + tag_dict["attributes"] = element.attrs + if element.contents: + children = [] + for child in element.contents: + if isinstance(child, str): + children.append(child.strip()) + else: + children.append(parse_element(child)) + if children: + tag_dict["children"] = children + return tag_dict - new_dom = [] - for element in soup.contents: - if isinstance(element, str) and not element.strip(): - # Skip empty text nodes - continue - elif isinstance(element, str): - # Treat remaining text nodes as separate elements for clarity - new_dom.append({"tag": "text", "content": element.strip()}) - else: - new_dom.append(parse_element(element)) + new_dom = [] + for element in soup.contents: + if isinstance(element, str) and not element.strip(): + continue + elif isinstance(element, str): + new_dom.append({"tag": "text", "content": element.strip()}) + else: + new_dom.append(parse_element(element)) - return new_dom + return new_dom # `import *` will give you these @@ -508,5 +428,4 @@ __all__ = [ "enrich_text_with_urls", "image_to_data_uri", "TelegraphAPI", - "create_ph_account", ] diff --git a/handlers/cohere.py b/handlers/cohere.py index b437fca..ddc0082 100644 --- a/handlers/cohere.py +++ b/handlers/cohere.py @@ -17,19 +17,12 @@ markdown_symbol.link = "🔗" # If you want, Customizing the link symbol COHERE_API_KEY = environ.get("COHERE_API_KEY") COHERE_MODEL = "command-r-plus" - -TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") -if TELEGRA_PH_TOKEN: - ph = TelegraphAPI(TELEGRA_PH_TOKEN) -else: - TELEGRA_PH_TOKEN = create_ph_account( - short_name="cohere", author_name="A Telegram Bot" - ) - ph = TelegraphAPI(TELEGRA_PH_TOKEN) - if COHERE_API_KEY: co = cohere.Client(api_key=COHERE_API_KEY) +TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") +ph = TelegraphAPI(TELEGRA_PH_TOKEN) + # Global history cache cohere_player_dict = ExpiringDict(max_len=1000, max_age_seconds=300) diff --git a/handlers/useful.py b/handlers/useful.py index 835adab..75bc760 100644 --- a/handlers/useful.py +++ b/handlers/useful.py @@ -19,20 +19,18 @@ from . import * from telegramify_markdown.customize import markdown_symbol +#### Cohere init #### import cohere COHERE_API_KEY = environ.get("COHERE_API_KEY") -TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") +COHERE_MODEL = "command-r-plus" if COHERE_API_KEY: co = cohere.Client(api_key=COHERE_API_KEY) -if TELEGRA_PH_TOKEN: - ph = TelegraphAPI(TELEGRA_PH_TOKEN) -else: - TELEGRA_PH_TOKEN = create_ph_account( - short_name="Answer it", author_name="A Telegram Bot" - ) - ph = TelegraphAPI(TELEGRA_PH_TOKEN) -COHERE_MODEL = "command-r-plus" + +#### Telegraph init #### +TELEGRA_PH_TOKEN = environ.get("TELEGRA_PH_TOKEN") +ph = TelegraphAPI(TELEGRA_PH_TOKEN) +#### Telegraph done #### chat_message_dict = ExpiringDict(max_len=100, max_age_seconds=120) chat_user_dict = ExpiringDict(max_len=100, max_age_seconds=20) @@ -203,7 +201,7 @@ def answer_it_handler(message: Message, bot: TeleBot): else: pass - ##### Answer ##### + ##### Telegraph ##### final_answer(latest_message, bot, full, chat_id_list) From 907b9d4fde6de445f300e2909401f4f8d26dd500 Mon Sep 17 00:00:00 2001 From: yihong0618 Date: Tue, 25 Jun 2024 19:22:09 +0800 Subject: [PATCH 7/7] fix: add to delete Signed-off-by: yihong0618 --- handlers/useful.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/handlers/useful.py b/handlers/useful.py index 75bc760..4727f04 100644 --- a/handlers/useful.py +++ b/handlers/useful.py @@ -24,6 +24,8 @@ import cohere COHERE_API_KEY = environ.get("COHERE_API_KEY") COHERE_MODEL = "command-r-plus" +# if you want to use cohere for answer it, set it to True +USE_CHHERE = False if COHERE_API_KEY: co = cohere.Client(api_key=COHERE_API_KEY) @@ -195,7 +197,7 @@ def answer_it_handler(message: Message, bot: TeleBot): chat_id_list.append(reply_id.message_id) ##### Cohere ##### - if COHERE_API_KEY: + if USE_CHHERE and COHERE_API_KEY: full, chat_id = cohere_answer(latest_message, bot, full, m) chat_id_list.append(chat_id) else: @@ -269,15 +271,15 @@ def cohere_answer(latest_message: Message, bot: TeleBot, full, m): return full, reply_id.message_id -def final_answer(latest_message: Message, bot: TeleBot, full, list): +def final_answer(latest_message: Message, bot: TeleBot, full, answers_list): """final answer""" who = "Answer" reply_id = bot_reply_first(latest_message, who, bot) ph_s = ph.create_page_md(title="Answer it", markdown_text=full) bot_reply_markdown(reply_id, who, f"[View]({ph_s})", bot) # delete the chat message, only leave a telegra.ph link - # for i in list: - # bot.delete_message(latest_message.chat.id, i) + for i in answers_list: + bot.delete_message(latest_message.chat.id, i) if GOOGLE_GEMINI_KEY and CHATGPT_API_KEY: