Compare commits
2 Commits
387606426c
...
543e139ca0
Author | SHA1 | Date | |
---|---|---|---|
543e139ca0 | |||
551c5ddc02 |
@ -1,78 +0,0 @@
|
|||||||
"""
|
|
||||||
This plugin provides a command to interact with the music knowledge A.I.
|
|
||||||
"""
|
|
||||||
# plugins/ai-music.py
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import simplematrixbotlib as botlib
|
|
||||||
import re
|
|
||||||
import markdown2
|
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
|
||||||
"""
|
|
||||||
Function to handle the !music command.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
room (Room): The Matrix room where the command was invoked.
|
|
||||||
message (RoomMessage): The message object containing the command.
|
|
||||||
bot (Bot): The bot object.
|
|
||||||
PREFIX (str): The command prefix.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("music"):
|
|
||||||
logging.info("Received !music command")
|
|
||||||
args = match.args()
|
|
||||||
if len(args) < 1:
|
|
||||||
await bot.api.send_text_message(room.room_id, "Usage: !music [prompt]")
|
|
||||||
logging.info("Sent usage message to the room")
|
|
||||||
return
|
|
||||||
|
|
||||||
prompt = ' '.join(args)
|
|
||||||
|
|
||||||
# Prepare data for the API request
|
|
||||||
url = "http://127.0.0.1:5000/v1/completions"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"prompt": "<s>[INST]You are FunguyFlows, an AI music expert bot with access to a vast repository of knowledge encompassing every facet of music, spanning genres, artists, bands, compositions, albums, lyrics, music theory, composition techniques, composers, music history, music appreciation, and more. Your role is to serve as an invaluable resource and guide within the realm of music, offering comprehensive insights, recommendations, and assistance to music enthusiasts, students, professionals, and curious minds alike.Drawing upon humanity's collective knowledge and expertise in music, your database contains a wealth of information sourced from authoritative texts, scholarly articles, historical archives, musical compositions, biographies, discographies, and cultural repositories. This rich repository enables you to provide accurate, detailed, and insightful responses to a wide range of inquiries, covering an extensive array of topics related to music theory, composition, performance, history, and appreciation.As an AI music expert, your knowledge extends across various genres, including classical, jazz, rock, pop, hip-hop, electronic, folk, world music, and beyond. You possess a deep understanding of musical concepts such as melody, harmony, rhythm, timbre, form, dynamics, and texture, allowing you to analyze and interpret musical compositions with precision and clarity.In addition to your expertise in music theory and composition, you are well-versed in the works of renowned composers throughout history, from the classical masters of Bach, Mozart, and Beethoven to contemporary innovators like John Williams, Philip Glass, and Hans Zimmer. You can provide detailed biographical information, analysis of their compositions, and insights into their lasting impact on the world of music.Your knowledge of music history is extensive, spanning centuries of cultural evolution and musical innovation. From the Gregorian chants of the medieval period to the avant-garde experiments of the 20th century, you can trace the development of musical styles, movements, and traditions across different regions and epochs, shedding light on the social, political, and artistic contexts that shaped musical expression throughout history.Furthermore, your expertise encompasses a diverse range of topics related to music appreciation, including techniques for active listening, critical analysis of musical performances, understanding musical genres and styles, exploring the cultural significance of music, and engaging with music as a form of creative expression, emotional communication, and cultural identity.Whether users seek recommendations for discovering new artists and albums, assistance with analyzing musical compositions, insights into music theory concepts, guidance on composing their own music, or historical context for understanding musical traditions, you are poised to provide informative, engaging, and enriching responses tailored to their interests and inquiries.As an AI music expert bot, your mission is to inspire curiosity, deepen understanding, and foster appreciation for the diverse and multifaceted world of music. By sharing your knowledge, passion, and enthusiasm for music, you aim to empower individuals to explore, create, and connect through the universal language of sound. Embrace your role as a trusted guide and mentor within the realm of music, and let your expertise illuminate the path for music lovers and learners alike, one harmonious interaction at a time. You will only answer questions about music, and nothing else. Now... tell me about: "+prompt+"[/INST]",
|
|
||||||
"max_tokens": 1024,
|
|
||||||
"temperature": 1.31,
|
|
||||||
"top_p": 0.14,
|
|
||||||
"top_k": 49,
|
|
||||||
"seed": -1,
|
|
||||||
"stream": False,
|
|
||||||
"repetition_penalty": 1.17
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make HTTP request to the API endpoint
|
|
||||||
try:
|
|
||||||
response = requests.post(url, headers=headers, json=data, verify=False)
|
|
||||||
response.raise_for_status() # Raise HTTPError for bad responses
|
|
||||||
payload = response.json()
|
|
||||||
new_text = payload['choices'][0]['text']
|
|
||||||
new_text = markdown_to_html(new_text)
|
|
||||||
print(new_text)
|
|
||||||
|
|
||||||
if new_text.count('<p>') > 1 or new_text.count('<li>') > 1: # Check if new_text has more than one paragraph
|
|
||||||
#new_text = new_text.replace("\n", '<br>')
|
|
||||||
#new_text = re.sub(r"\*\*(.*?)\*\*", r"<strong>\1</strong>", new_text)
|
|
||||||
new_text = "<details><summary><strong>🎵Funguy Music GPT🎵<br>⤵︎Click Here To See Funguy's Response⤵︎</strong></summary>" + new_text + "</details>"
|
|
||||||
await bot.api.send_markdown_message(room.room_id, new_text)
|
|
||||||
else:
|
|
||||||
await bot.api.send_markdown_message(room.room_id, new_text)
|
|
||||||
logging.info("Sent generated text to the room")
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
logging.error(f"HTTP request failed for '{prompt}': {e}")
|
|
||||||
await bot.api.send_text_message(room.room_id, f"Error generating text: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def markdown_to_html(markdown_text):
|
|
||||||
html_content = markdown2.markdown(markdown_text)
|
|
||||||
return html_content
|
|
@ -1,76 +0,0 @@
|
|||||||
"""
|
|
||||||
This plugin provides a command to interact with the LLM for Tech/IT/Security/Selfhosting/Programming etc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# plugins/ai-tech.py
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import simplematrixbotlib as botlib
|
|
||||||
import re
|
|
||||||
import markdown2
|
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
|
||||||
"""
|
|
||||||
Function to handle the !funguy command.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
room (Room): The Matrix room where the command was invoked.
|
|
||||||
message (RoomMessage): The message object containing the command.
|
|
||||||
bot (Bot): The bot object.
|
|
||||||
prefix (str): The command prefix.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("funguy"):
|
|
||||||
logging.info("Received !funguy command")
|
|
||||||
args = match.args()
|
|
||||||
if len(args) < 1:
|
|
||||||
await bot.api.send_text_message(room.room_id, "Usage: !funguy [prompt]")
|
|
||||||
logging.info("Sent usage message to the room")
|
|
||||||
return
|
|
||||||
|
|
||||||
prompt = ' '.join(args)
|
|
||||||
|
|
||||||
# Prepare data for the API request
|
|
||||||
url = "http://127.0.0.1:5000/v1/completions"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"prompt": "<s>[INST]You are FunguyGPT, a language model deployed as a chatbot for a community chat room hosted on Matrix. The chat room focuses on discussions related to self-hosting, system administration, cybersecurity, homelab setups, programming, coding, and general IT/tech topics. Your role is to assist users within the community by providing helpful responses and guidance on various technical matters. It's essential to keep your replies concise and relevant, addressing users' queries effectively while maintaining a friendly and approachable demeanor. Remember to prioritize clarity and brevity in your interactions to ensure a positive user experience within the chat room environment. You are FunguyGPT, an AI language model designed to serve as a chatbot within a vibrant and diverse community chat room hosted on the Matrix platform. This chat room acts as a hub for enthusiasts and professionals alike, engaging in discussions spanning a wide array of technical topics, including self-hosting, system administration, cybersecurity, homelab setups, programming, coding, and general IT/tech inquiries. Your primary objective is to act as a reliable and knowledgeable assistant, offering assistance, guidance, and solutions to the community members as they navigate through their technical challenges and endeavors.Given the broad spectrum of topics discussed within the community, it's crucial for you to possess a comprehensive understanding of various domains within the realm of technology. As such, your knowledge should encompass not only the fundamentals of programming languages, software development methodologies, and system administration principles but also extend to cybersecurity best practices, networking protocols, cloud computing, database management, and beyond. Your role as a chatbot is multifaceted and dynamic. You'll be tasked with responding to a wide range of queries, ranging from beginner-level inquiries seeking clarification on basic concepts to advanced discussions requiring nuanced insights and problem-solving skills. Whether it's troubleshooting code errors, configuring network settings, securing server environments, optimizing database performance, or recommending suitable homelab hardware, your goal is to provide accurate, actionable, and helpful responses tailored to the needs of the community members. In addition to offering direct assistance, you should also strive to foster a collaborative and supportive atmosphere within the chat room. Encourage knowledge sharing, facilitate discussions, and celebrate the achievements of community members as they tackle technical challenges and embark on learning journeys. By promoting a culture of learning and collaboration, you'll contribute to the growth and cohesion of the community, empowering individuals to expand their skill sets and achieve their goals within the realm of technology. As you engage with users within the chat room, prioritize brevity and clarity in your responses. While it's essential to provide comprehensive and accurate information, it's equally important to convey it in a concise and easily understandable manner. Avoid overly technical jargon or convoluted explanations that may confuse or overwhelm community members, opting instead for straightforward explanations and practical solutions whenever possible. Remember, your ultimate objective is to be a trusted ally and resource for the members of the community as they navigate the ever-evolving landscape of technology. By leveraging your expertise, empathy, and problem-solving abilities, you'll play a vital role in facilitating knowledge exchange, fostering collaboration, and empowering individuals to succeed in their technical endeavors. As you embark on this journey as a chatbot within the Matrix community, embrace the opportunity to make a meaningful and positive impact, one helpful interaction at a time. You will format the reply using minimal html instead of markdown. Do not use markdown formatting. Here is the prompt: "+prompt+"[/INST]",
|
|
||||||
"max_tokens": 1024,
|
|
||||||
"temperature": 1.31,
|
|
||||||
"top_p": 0.14,
|
|
||||||
"top_k": 49,
|
|
||||||
"seed": -1,
|
|
||||||
"stream": False,
|
|
||||||
"repetition_penalty": 1.17
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make HTTP request to the API endpoint
|
|
||||||
try:
|
|
||||||
response = requests.post(url, headers=headers, json=data, verify=False)
|
|
||||||
response.raise_for_status() # Raise HTTPError for bad responses
|
|
||||||
payload = response.json()
|
|
||||||
new_text = payload['choices'][0]['text']
|
|
||||||
new_text = markdown_to_html(new_text)
|
|
||||||
if new_text.count('<p>') > 2: # Check if new_text has more than one paragraph
|
|
||||||
#new_text = new_text.replace("\n", '<br>')
|
|
||||||
#new_text = re.sub(r"\*\*(.*?)\*\*", r"<strong>\1</strong>", new_text)
|
|
||||||
new_text = "<details><summary><strong>🍄Funguy Tech GPT🍄<br>⤵︎Click Here To See Funguy's Response⤵︎</strong></summary>" + new_text + "</details>"
|
|
||||||
await bot.api.send_markdown_message(room.room_id, new_text)
|
|
||||||
else:
|
|
||||||
await bot.api.send_markdown_message(room.room_id, new_text)
|
|
||||||
logging.info(f"Sent generated text to the room: {new_text}")
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
logging.error(f"HTTP request failed for '{prompt}': {e}")
|
|
||||||
await bot.api.send_text_message(room.room_id, f"Error generating text: {e}")
|
|
||||||
|
|
||||||
def markdown_to_html(markdown_text):
|
|
||||||
html_content = markdown2.markdown(markdown_text)
|
|
||||||
return html_content
|
|
1226
plugins/ai.json
Normal file
1226
plugins/ai.json
Normal file
File diff suppressed because it is too large
Load Diff
109
plugins/ai.py
Normal file
109
plugins/ai.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
This plugin provides commands to interact with different AI models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import simplematrixbotlib as botlib
|
||||||
|
import re
|
||||||
|
import markdown2
|
||||||
|
|
||||||
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Function to handle AI commands.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (Bot): The bot object.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
|
if match.is_not_from_this_bot() and match.prefix():
|
||||||
|
logging.info(f"Received command: {match.command()}")
|
||||||
|
|
||||||
|
command = match.command()
|
||||||
|
conf = load_config()
|
||||||
|
if command in conf:
|
||||||
|
await handle_ai_command(room, bot, command, match.args(), conf)
|
||||||
|
#else:
|
||||||
|
#await bot.api.send_text_message(room.room_id, f"Unknown command: {command}")
|
||||||
|
#logging.info("Sent unknown command message to the room")
|
||||||
|
|
||||||
|
async def handle_ai_command(room, bot, command, args, config):
|
||||||
|
"""
|
||||||
|
Function to handle AI commands.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
bot (Bot): The bot object.
|
||||||
|
command (str): The name of the AI model command.
|
||||||
|
args (list): List of arguments provided with the command.
|
||||||
|
config: Configuration parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if len(args) < 1:
|
||||||
|
await bot.api.send_text_message(room.room_id, f"Usage: !{command} [prompt]")
|
||||||
|
logging.info("Sent usage message to the room")
|
||||||
|
return
|
||||||
|
|
||||||
|
prompt = ' '.join(args)
|
||||||
|
|
||||||
|
# Prepare data for the API request
|
||||||
|
url = "http://127.0.0.1:5000/v1/completions"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"prompt": f"<s>[INST]{config[command]['prompt']}{prompt}[/INST]",
|
||||||
|
"max_tokens": 1024,
|
||||||
|
"temperature": config[command]["temperature"],
|
||||||
|
"top_p": config[command]["top_p"],
|
||||||
|
"top_k": config[command]["top_k"],
|
||||||
|
"repetition_penalty": config[command]["repetition_penalty"],
|
||||||
|
"seed": -1,
|
||||||
|
"stream": False
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make HTTP request to the API endpoint
|
||||||
|
try:
|
||||||
|
response = requests.post(url, headers=headers, json=data, verify=False)
|
||||||
|
response.raise_for_status() # Raise HTTPError for bad responses
|
||||||
|
payload = response.json()
|
||||||
|
new_text = payload['choices'][0]['text']
|
||||||
|
new_text = markdown_to_html(new_text)
|
||||||
|
|
||||||
|
if new_text.count('<p>') > 1 or new_text.count('<li>') > 1: # Check if new_text has more than one paragraph
|
||||||
|
new_text = f"<details><summary><strong>{config[command]['summary']}</strong></summary>{new_text}</details>"
|
||||||
|
await bot.api.send_markdown_message(room.room_id, new_text)
|
||||||
|
else:
|
||||||
|
await bot.api.send_markdown_message(room.room_id, new_text)
|
||||||
|
logging.info("Sent generated text to the room")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error(f"HTTP request failed for '{prompt}': {e}")
|
||||||
|
await bot.api.send_text_message(room.room_id, f"Error generating text: {e}")
|
||||||
|
|
||||||
|
def markdown_to_html(markdown_text):
|
||||||
|
html_content = markdown2.markdown(markdown_text)
|
||||||
|
return html_content
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
"""
|
||||||
|
Load configuration from ai.json file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Configuration parameters.
|
||||||
|
"""
|
||||||
|
with open("plugins/ai.json", "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config
|
||||||
|
|
||||||
|
CONFIG = load_config()
|
||||||
|
|
@ -1,24 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
This plugin provides a command to display the list of available commands and their descriptions.
|
Plugin for providing a command to display the list of available commands and their descriptions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# plugins/help.py
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import simplematrixbotlib as botlib
|
import simplematrixbotlib as botlib
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
"""
|
"""
|
||||||
Function to handle the !help command.
|
Function to handle the !help command.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
room (Room): The Matrix room where the command was invoked.
|
room (Room): The Matrix room where the command was invoked.
|
||||||
message (RoomMessage): The message object containing the command.
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("help"):
|
if match.is_not_from_this_bot() and match.prefix() and match.command("help"):
|
||||||
logging.info("Fetching command help documentation")
|
logging.info("Fetching command help documentation")
|
||||||
commands_message = """
|
commands_message = """
|
||||||
@ -33,11 +34,11 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>💻 <strong>!proxy</strong></summary>
|
<details><summary>💻 <strong>!proxy</strong></summary>
|
||||||
<p>Retrieves a tested/working random SOCKS5 proxy. Fetches a list of SOCKS5 proxies, tests their availability, and sends the first working proxy to the chat room.</p>
|
<p>Retrieves a tested/working random SOCKS5 proxy. Fetches a list of SOCKS5 proxies, tests their availability, and sends the first working proxy to the chat room.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>📶 <strong>!isup [domain/ip]</strong></summary>
|
<details><summary>📶 <strong>!isup [domain/ip]</strong></summary>
|
||||||
<p>Checks if the specified domain or IP address is reachable. Checks if the specified domain or IP address is reachable by attempting to ping it. If DNS resolution is successful, it checks HTTP and HTTPS service availability by sending requests to the domain.</p>
|
<p>Checks if the specified domain or IP address is reachable. Checks if the specified domain or IP address is reachable by attempting to ping it. If DNS resolution is successful, it checks HTTP and HTTPS service availability by sending requests to the domain.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>☯ <strong>!karma [user]</strong></summary>
|
<details><summary>☯ <strong>!karma [user]</strong></summary>
|
||||||
@ -52,12 +53,16 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
<p>Decreases the karma points for the specified user by 1. Decreases the karma points for the specified user by 1 in the database and sends the updated points as a message to the chat room.</p>
|
<p>Decreases the karma points for the specified user by 1. Decreases the karma points for the specified user by 1 in the database and sends the updated points as a message to the chat room.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>📄 <strong>!funguy [prompt]</strong></summary>
|
<details><summary>📸 <strong>!sd [prompt]</strong></summary>
|
||||||
<p>An AI large language model designed to serve as a chatbot within a vibrant and diverse community chat room hosted on the Matrix platform. (Currently loaded model: <strong>TheBloke_Mistral-7B-Instruct-v0.2-GPTQ</strong>)</p>
|
<p>Generates images using self-hosted Stable Diffusion. See available options using just '!sd'.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>🎝 <strong>!music [prompt]</strong></summary>
|
<details><summary>💡 <strong>!enable</strong></summary>
|
||||||
<p>Your music expert! Try it out.</p>
|
<p>Enables a disabled command. Use '!enable plugin room' to enable a specific command.</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>🚫 <strong>!disable</strong></summary>
|
||||||
|
<p>Disables a command. Use '!disable plugin room' to disable a specific command.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary>🌟 <strong>Funguy Bot Credits</strong> 🌟</summary>
|
<details><summary>🌟 <strong>Funguy Bot Credits</strong> 🌟</summary>
|
||||||
@ -66,7 +71,11 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
</details>
|
</details>
|
||||||
</p>
|
</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details><summary>📸 <strong>Funguy Bot AI Commands</strong> 🌟</summary>
|
||||||
|
!tech, !music, !eth, !seo, !term, !eng, !intv, !js, !xls, !pron, !spk, !trv, !plag, !char, !adv, !story, !foot, !comic, !motiv, !music, !debate, !write, !script, !author, !crit, !rel, !poem, !rap, !speak, !phil, !math, !tutor, !design, !sec, !recruit, !coach, !etymo, !com, !magic, !counsel, !behavior, !fit, !mh, !realest, !log, !dental, !web, !health, !acc, !chef, !auto, !art, !fin, !invest, !tea, !interior, !florist, !selfhelp, !gnome, !aph, !adv, !advgame, !esc, !title, !stats, !prompt, !teach, !db, !diet, !psych, !domain, !tech, !review, !devrel, !acad, !arch, !insane, !manip, !logic, !review, !diy, !influencer, !philos, !socrat, !edu, !meditate, !writer, !smm, !eloc, !viz, !nav, !hypno, !hist, !astro, !critic, !comp, !journo, !curation, !pscoach, !makeup, !childcare, !writing, !art, !py, !syn, !shop, !dining, !telemed, !cook, !law, !fashion, !ml, !trans, !design, !it, !chess, !prompt, !dev, !math, !regex, !time, !dream, !coach, !r, !comm, !trans, !php, !emergency, !worksheet, !test, !game, !security, !create, !browse, !dev, !search, !startup, !guide, !langdet, !sales, !msg, !ceo, !diag, !coach, !therapy, !legal, !gen, !mgmt, !drunk, !hist, !rec, !write, !techtrans, !ai, !game, !proof, !spirit, !spirit, !chem, !friend, !py, !chat, !wiki, !kanji, !note, !litcrit, !enhance, !travel, !data, !gaming
|
||||||
|
</details>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await bot.api.send_markdown_message(room.room_id, commands_message)
|
await bot.api.send_markdown_message(room.room_id, commands_message)
|
||||||
logging.info("Sent help documentation to the room")
|
logging.info("Sent help documentation to the room")
|
||||||
|
@ -1,35 +1,64 @@
|
|||||||
"""
|
"""
|
||||||
This plugin provides a command for the admin to load a plugin
|
Plugin for providing a command for the admin to load a plugin.
|
||||||
"""
|
"""
|
||||||
# plugins/load_plugin.py
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import importlib
|
import importlib
|
||||||
import simplematrixbotlib as botlib
|
import simplematrixbotlib as botlib
|
||||||
|
|
||||||
|
# Dictionary to store loaded plugins
|
||||||
PLUGINS = {}
|
PLUGINS = {}
|
||||||
|
|
||||||
async def load_plugin(plugin_name):
|
async def load_plugin(plugin_name):
|
||||||
|
"""
|
||||||
|
Asynchronously loads a plugin.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_name (str): The name of the plugin to load.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the plugin is loaded successfully, False otherwise.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Import the plugin module
|
||||||
module = importlib.import_module(f"plugins.{plugin_name}")
|
module = importlib.import_module(f"plugins.{plugin_name}")
|
||||||
|
# Add the plugin module to the PLUGINS dictionary
|
||||||
PLUGINS[plugin_name] = module
|
PLUGINS[plugin_name] = module
|
||||||
logging.info(f"Loaded plugin: {plugin_name}")
|
logging.info(f"Loaded plugin: {plugin_name}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Log an error if the plugin fails to load
|
||||||
logging.error(f"Error loading plugin {plugin_name}: {e}")
|
logging.error(f"Error loading plugin {plugin_name}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Asynchronously handles the command to load a plugin.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("load"):
|
if match.is_not_from_this_bot() and match.prefix() and match.command("load"):
|
||||||
|
# Check if the sender is the admin
|
||||||
if str(message.sender) == config.admin_user:
|
if str(message.sender) == config.admin_user:
|
||||||
args = match.args()
|
args = match.args()
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
|
# Send usage message if the command format is incorrect
|
||||||
await bot.api.send_text_message(room.room_id, "Usage: !load <plugin>")
|
await bot.api.send_text_message(room.room_id, "Usage: !load <plugin>")
|
||||||
else:
|
else:
|
||||||
plugin_name = args[0]
|
plugin_name = args[0]
|
||||||
|
# Check if the plugin is not already loaded
|
||||||
if plugin_name not in PLUGINS:
|
if plugin_name not in PLUGINS:
|
||||||
|
# Load the plugin
|
||||||
success = await load_plugin(plugin_name)
|
success = await load_plugin(plugin_name)
|
||||||
if success:
|
if success:
|
||||||
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' loaded successfully")
|
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' loaded successfully")
|
||||||
@ -38,4 +67,5 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
else:
|
else:
|
||||||
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' is already loaded")
|
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' is already loaded")
|
||||||
else:
|
else:
|
||||||
|
# Send unauthorized message if the sender is not the admin
|
||||||
await bot.api.send_text_message(room.room_id, "You are not authorized to load plugins.")
|
await bot.api.send_text_message(room.room_id, "You are not authorized to load plugins.")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
This plugin provides a command to generate images using self hosted Stable Diffusion and send to the room
|
Plugin for generating images using self-hosted Stable Diffusion and sending them to a Matrix chat room.
|
||||||
"""
|
"""
|
||||||
# plugins/stable-diffusion.py
|
|
||||||
import requests
|
import requests
|
||||||
import base64
|
import base64
|
||||||
from asyncio import Queue
|
from asyncio import Queue
|
||||||
@ -11,17 +11,45 @@ import markdown2
|
|||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
def slugify_prompt(prompt):
|
def slugify_prompt(prompt):
|
||||||
# Generate a slug from the prompt
|
"""
|
||||||
|
Generates a URL-friendly slug from the given prompt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt (str): The prompt to slugify.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A URL-friendly slug version of the prompt.
|
||||||
|
"""
|
||||||
return slugify(prompt)
|
return slugify(prompt)
|
||||||
|
|
||||||
# Queue to store pending commands
|
# Queue to store pending commands
|
||||||
command_queue = Queue()
|
command_queue = Queue()
|
||||||
|
|
||||||
def markdown_to_html(markdown_text):
|
def markdown_to_html(markdown_text):
|
||||||
|
"""
|
||||||
|
Converts Markdown text to HTML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
markdown_text (str): The Markdown text to convert.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The HTML version of the input Markdown text.
|
||||||
|
"""
|
||||||
html_content = markdown2.markdown(markdown_text)
|
html_content = markdown2.markdown(markdown_text)
|
||||||
return html_content
|
return html_content
|
||||||
|
|
||||||
async def process_command(room, message, bot, prefix, config):
|
async def process_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Asynchronously processes the commands received in the Matrix room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was received.
|
||||||
|
message (Message): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
|
||||||
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
if match.prefix() and match.command("sd"):
|
if match.prefix() and match.command("sd"):
|
||||||
if command_queue.empty():
|
if command_queue.empty():
|
||||||
@ -30,10 +58,21 @@ async def process_command(room, message, bot, prefix, config):
|
|||||||
await command_queue.put((room, message, bot, prefix, config))
|
await command_queue.put((room, message, bot, prefix, config))
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Asynchronously handles the 'sd' command, which generates images using Stable Diffusion.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was received.
|
||||||
|
message (Message): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
if match.prefix() and match.command("sd"):
|
if match.prefix() and match.command("sd"):
|
||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description='Generate images using self hosted Stable Diffusion')
|
# Parse command-line arguments
|
||||||
|
parser = argparse.ArgumentParser(description='Generate images using self-hosted Stable Diffusion')
|
||||||
parser.add_argument('--steps', type=int, default=4, help='Number of steps, default=16')
|
parser.add_argument('--steps', type=int, default=4, help='Number of steps, default=16')
|
||||||
parser.add_argument('--cfg', type=int, default=1.25, help='CFG scale, default=7')
|
parser.add_argument('--cfg', type=int, default=1.25, help='CFG scale, default=7')
|
||||||
parser.add_argument('--h', type=int, default=512, help='Height of the image, default=512')
|
parser.add_argument('--h', type=int, default=512, help='Height of the image, default=512')
|
||||||
@ -61,32 +100,46 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
}
|
}
|
||||||
url = "http://127.0.0.1:7860/sdapi/v1/txt2img"
|
url = "http://127.0.0.1:7860/sdapi/v1/txt2img"
|
||||||
|
|
||||||
|
# Send request to the Stable Diffusion API
|
||||||
response = requests.post(url=url, json=payload)
|
response = requests.post(url=url, json=payload)
|
||||||
r = response.json()
|
r = response.json()
|
||||||
|
|
||||||
# Slugify the prompt
|
# Slugify the prompt
|
||||||
prompt_slug = prompt[:120]
|
prompt_slug = slugify(prompt[:120])
|
||||||
|
|
||||||
# Construct filename
|
# Construct filename
|
||||||
filename = f"{prompt_slug}_{args.steps}_{args.h}x{args.w}_{sampler_name}_{args.cfg}.jpg"
|
filename = f"{prompt_slug}_{args.steps}_{args.h}x{args.w}_{sampler_name}_{args.cfg}.jpg"
|
||||||
with open(f"/tmp/{filename}", 'wb') as f:
|
with open(f"/tmp/{filename}", 'wb') as f:
|
||||||
f.write(base64.b64decode(r['images'][0]))
|
f.write(base64.b64decode(r['images'][0]))
|
||||||
await bot.api.send_image_message(room_id=room.room_id, image_filepath=f"/tmp/{filename}") # Corrected argument name
|
|
||||||
|
# Send the generated image to the Matrix room
|
||||||
|
await bot.api.send_image_message(room_id=room.room_id, image_filepath=f"/tmp/{filename}")
|
||||||
|
|
||||||
|
# Format and send information about the generated image
|
||||||
neg = neg.replace(" ", "")
|
neg = neg.replace(" ", "")
|
||||||
info_msg = f"""<details><summary>🍄⤵︎Image Info:⤵︎</summary><strong>Prompt:</strong> {prompt_slug}<br><strong>Steps:</strong> {args.steps}<br><strong>Dimensions:</strong> {args.h}x{args.w}<br><strong>Sampler:</strong> {sampler_name}<br><strong>CFG Scale:</strong> {args.cfg}<br><strong>Negative Prompt: {neg}</strong></details>"""
|
info_msg = f"""<details><summary>🍄⤵︎Image Info:⤵︎</summary><strong>Prompt:</strong> {prompt_slug}<br><strong>Steps:</strong> {args.steps}<br><strong>Dimensions:</strong> {args.h}x{args.w}<br><strong>Sampler:</strong> {sampler_name}<br><strong>CFG Scale:</strong> {args.cfg}<br><strong>Negative Prompt: {neg}</strong></details>"""
|
||||||
await bot.api.send_markdown_message(room.room_id, info_msg)
|
await bot.api.send_markdown_message(room.room_id, info_msg)
|
||||||
except argparse.ArgumentError as e:
|
except argparse.ArgumentError as e:
|
||||||
|
# Handle argument errors
|
||||||
await bot.api.send_text_message(room.room_id, f"Error: {e}")
|
await bot.api.send_text_message(room.room_id, f"Error: {e}")
|
||||||
await bot.api.send_markdown_message(room.room_id, "<details><summary>Stable Diffusion Help</summary>" + print_help() + "</details>")
|
await bot.api.send_markdown_message(room.room_id, "<details><summary>Stable Diffusion Help</summary>" + print_help() + "</details>")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Handle general errors
|
||||||
await bot.api.send_text_message(room.room_id, f"Error processing the command: {str(e)}")
|
await bot.api.send_text_message(room.room_id, f"Error processing the command: {str(e)}")
|
||||||
finally:
|
finally:
|
||||||
|
# Process next command from the queue, if any
|
||||||
if not command_queue.empty():
|
if not command_queue.empty():
|
||||||
next_command = await command_queue.get()
|
next_command = await command_queue.get()
|
||||||
await handle_command(*next_command)
|
await handle_command(*next_command)
|
||||||
|
|
||||||
|
|
||||||
def print_help():
|
def print_help():
|
||||||
|
"""
|
||||||
|
Generates help text for the 'sd' command.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Help text for the 'sd' command.
|
||||||
|
"""
|
||||||
return """
|
return """
|
||||||
<p>Generate images using self-hosted Stable Diffusion</p>
|
<p>Generate images using self-hosted Stable Diffusion</p>
|
||||||
|
|
||||||
@ -95,6 +148,8 @@ def print_help():
|
|||||||
<li>prompt - Prompt for the image</li>
|
<li>prompt - Prompt for the image</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p>Default Negative Prompts: ((((ugly)))), (((duplicate))), ((morbid)), ((mutilated)), out of frame, extra fingers, mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), ((ugly)), blurry, ((bad anatomy)), (((bad proportions))), ((extra limbs)), cloned face, (((disfigured))), out of frame, ugly, extra limbs, (bad anatomy), gross proportions, (malformed limbs), ((missing arms)), ((missing legs)), (((extra arms))), (((extra legs))), mutated hands, (fused fingers), (too many fingers), (((long neck)))</p>
|
||||||
|
|
||||||
<p>Optional arguments:</p>
|
<p>Optional arguments:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>--steps STEPS - Number of steps, default=16</li>
|
<li>--steps STEPS - Number of steps, default=16</li>
|
||||||
|
@ -1,21 +1,39 @@
|
|||||||
"""
|
"""
|
||||||
This plugin provides a command to fetch YouTube video information from links.
|
Plugin for providing a command to fetch YouTube video information from links.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# plugins/youtube.py
|
# Importing necessary libraries
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import asyncio
|
||||||
from pytubefix import YouTube
|
from pytubefix import YouTube
|
||||||
import simplematrixbotlib as botlib
|
import simplematrixbotlib as botlib
|
||||||
import asyncio
|
|
||||||
|
|
||||||
def seconds_to_minutes_seconds(seconds):
|
def seconds_to_minutes_seconds(seconds):
|
||||||
|
"""
|
||||||
|
Converts seconds to a string representation of minutes and seconds.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seconds (int): The number of seconds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A string representation of minutes and seconds in the format MM:SS.
|
||||||
|
"""
|
||||||
minutes = seconds // 60
|
minutes = seconds // 60
|
||||||
seconds %= 60
|
seconds %= 60
|
||||||
return f"{minutes:02d}:{seconds:02d}"
|
return f"{minutes:02d}:{seconds:02d}"
|
||||||
|
|
||||||
async def fetch_youtube_info(youtube_url):
|
async def fetch_youtube_info(youtube_url):
|
||||||
|
"""
|
||||||
|
Asynchronously fetches information about a YouTube video.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
youtube_url (str): The URL of the YouTube video.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A message containing information about the YouTube video.
|
||||||
|
None if an error occurs during fetching.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
video = YouTube(youtube_url)
|
video = YouTube(youtube_url)
|
||||||
title = video.title
|
title = video.title
|
||||||
@ -31,8 +49,21 @@ async def fetch_youtube_info(youtube_url):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Asynchronously handles the command to fetch YouTube video information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
if match.is_not_from_this_bot() and re.search(r'youtube\.com/watch\?v=', message.body): # and room.room_id != '!uFhErnfpYhhlauJsNK:matrix.org':
|
if match.is_not_from_this_bot() and re.search(r'youtube\.com/watch\?v=', message.body):
|
||||||
logging.info("YouTube link detected")
|
logging.info("YouTube link detected")
|
||||||
video_id_match = re.search(r'youtube\.com/watch\?v=([^\s]+)', message.body)
|
video_id_match = re.search(r'youtube\.com/watch\?v=([^\s]+)', message.body)
|
||||||
if video_id_match:
|
if video_id_match:
|
||||||
@ -52,5 +83,3 @@ async def handle_command(room, message, bot, prefix, config):
|
|||||||
await asyncio.sleep(1) # wait for 1 second before retrying
|
await asyncio.sleep(1) # wait for 1 second before retrying
|
||||||
else:
|
else:
|
||||||
logging.error("Failed to fetch YouTube video information after retries")
|
logging.error("Failed to fetch YouTube video information after retries")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
This plugin provides a command to search for YouTube videos in the room
|
Plugin for providing a command to search for YouTube videos in the room.
|
||||||
"""
|
"""
|
||||||
# plugins/youtube_search.py
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import simplematrixbotlib as botlib
|
import simplematrixbotlib as botlib
|
||||||
from youtube_search import YoutubeSearch
|
from youtube_search import YoutubeSearch
|
||||||
|
|
||||||
async def handle_command(room, message, bot, PREFIX, config):
|
async def handle_command(room, message, bot, PREFIX, config):
|
||||||
|
"""
|
||||||
|
Asynchronously handles the command to search for YouTube videos in the room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
PREFIX (str): The command prefix.
|
||||||
|
config (dict): The bot's configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("yt"):
|
if match.is_not_from_this_bot() and match.prefix() and match.command("yt"):
|
||||||
args = match.args()
|
args = match.args()
|
||||||
@ -16,7 +28,7 @@ async def handle_command(room, message, bot, PREFIX, config):
|
|||||||
else:
|
else:
|
||||||
search_terms = " ".join(args)
|
search_terms = " ".join(args)
|
||||||
logging.info(f"Performing YouTube search for: {search_terms}")
|
logging.info(f"Performing YouTube search for: {search_terms}")
|
||||||
results = YoutubeSearch(search_terms, max_results=3).to_dict()
|
results = YoutubeSearch(search_terms, max_results=10).to_dict()
|
||||||
if results:
|
if results:
|
||||||
output = generate_output(results)
|
output = generate_output(results)
|
||||||
await send_collapsible_message(room, bot, output)
|
await send_collapsible_message(room, bot, output)
|
||||||
@ -24,6 +36,15 @@ async def handle_command(room, message, bot, PREFIX, config):
|
|||||||
await bot.api.send_text_message(room.room_id, "No results found.")
|
await bot.api.send_text_message(room.room_id, "No results found.")
|
||||||
|
|
||||||
def generate_output(results):
|
def generate_output(results):
|
||||||
|
"""
|
||||||
|
Generates HTML output for displaying YouTube search results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
results (list): A list of dictionaries containing information about YouTube videos.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: HTML formatted output containing YouTube search results.
|
||||||
|
"""
|
||||||
output = ""
|
output = ""
|
||||||
for video in results:
|
for video in results:
|
||||||
output += f'<a href="https://www.youtube.com/watch?v={video["id"]}">'
|
output += f'<a href="https://www.youtube.com/watch?v={video["id"]}">'
|
||||||
@ -37,5 +58,16 @@ def generate_output(results):
|
|||||||
|
|
||||||
|
|
||||||
async def send_collapsible_message(room, bot, content):
|
async def send_collapsible_message(room, bot, content):
|
||||||
|
"""
|
||||||
|
Sends a collapsible message containing YouTube search results to the room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the message will be sent.
|
||||||
|
bot (MatrixBot): The Matrix bot instance.
|
||||||
|
content (str): HTML content to be included in the collapsible message.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
message = f'<details><summary><strong>🍄Funguy ▶YouTube Search🍄<br>⤵︎Click Here To See Results⤵︎</strong></summary>{content}</details>'
|
message = f'<details><summary><strong>🍄Funguy ▶YouTube Search🍄<br>⤵︎Click Here To See Results⤵︎</strong></summary>{content}</details>'
|
||||||
await bot.api.send_markdown_message(room.room_id, message)
|
await bot.api.send_markdown_message(room.room_id, message)
|
||||||
|
Loading…
Reference in New Issue
Block a user