Stable diffusion help text fixed. Whois plugin added

This commit is contained in:
2026-04-28 00:03:34 -05:00
parent 6f86fe679f
commit 634ae1c066
4 changed files with 491 additions and 6 deletions
+95
View File
@@ -0,0 +1,95 @@
"""
Plugin for generating text using Ollama's Mistral 7B Instruct model and sending it to a Matrix chat room.
"""
import requests
from asyncio import Queue
import simplematrixbotlib as botlib
import argparse
# Queue to store pending commands
command_queue = Queue()
API_URL = "http://localhost:11434/api/generate"
MODEL_NAME = "mistral:7b-instruct"
async def process_command(room, message, bot, prefix, config):
"""
Queue and process !text commands sequentially.
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.prefix() and match.command("text"):
if command_queue.empty():
await handle_command(room, message, bot, prefix, config)
else:
await command_queue.put((room, message, bot, prefix, config))
async def handle_command(room, message, bot, prefix, config):
"""
Send the prompt to Ollama API and return the generated text.
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if not (match.prefix() and match.command("text")):
return
# Parse optional arguments
parser = argparse.ArgumentParser(description='Generate text using Ollama API')
parser.add_argument('--max_tokens', type=int, default=512, help='Maximum tokens to generate')
parser.add_argument('--temperature', type=float, default=0.7, help='Temperature for generation')
parser.add_argument('prompt', nargs='+', help='Prompt for the model')
try:
args = parser.parse_args(message.body.split()[1:]) # Skip command itself
prompt = ' '.join(args.prompt).strip()
if not prompt:
await bot.api.send_text_message(room.room_id, "Usage: !text <your prompt here>")
return
payload = {
"model": MODEL_NAME,
"prompt": prompt,
"max_tokens": args.max_tokens,
"temperature": args.temperature,
"stream": False
}
response = requests.post(API_URL, json=payload, timeout=60)
response.raise_for_status()
r = response.json()
generated_text = r.get("response", "").strip()
if not generated_text:
generated_text = "(No response from model)"
await bot.api.send_text_message(room.room_id, generated_text)
except argparse.ArgumentError as e:
await bot.api.send_text_message(room.room_id, f"Argument error: {e}")
except requests.exceptions.RequestException as e:
await bot.api.send_text_message(room.room_id, f"Error connecting to Ollama API: {e}")
except Exception as e:
await bot.api.send_text_message(room.room_id, f"Unexpected error: {e}")
finally:
# Process next command from the queue, if any
if not command_queue.empty():
next_command = await command_queue.get()
await handle_command(*next_command)
def print_help():
"""
Generates help text for the !text command.
"""
return """
<p>Generate text using Ollama's Mistral 7B Instruct model</p>
<p>Usage:</p>
<ul>
<li>!text <prompt> - Basic prompt for the model</li>
<li>Optional arguments:</li>
<ul>
<li>--max_tokens MAX_TOKENS - Maximum tokens to generate (default 512)</li>
<li>--temperature TEMPERATURE - Sampling temperature (default 0.7)</li>
</ul>
</ul>
"""
+187
View File
@@ -0,0 +1,187 @@
"""
Plugin for providing a command to fetch YouTube video information from links.
"""
# Importing necessary libraries
import re
import logging
import asyncio
import aiohttp
import yt_dlp
import simplematrixbotlib as botlib
from youtube_title_parse import get_artist_title
LYRICIST_API_URL = "https://lyrist.vercel.app/api/{}/{}"
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
seconds %= 60
return f"{minutes:02d}:{seconds:02d}"
async def fetch_lyrics(song, artist):
"""
Asynchronously fetches lyrics for a song from the Lyricist API.
Args:
song (str): The name of the song.
artist (str): The name of the artist.
Returns:
str: Lyrics of the song.
None if an error occurs during fetching.
"""
try:
async with aiohttp.ClientSession() as session:
url = LYRICIST_API_URL.format(artist, song)
logging.info(f"Fetching lyrics from: {url}")
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
if response.status == 200:
data = await response.json()
return data.get("lyrics")
else:
logging.warning(f"Lyrics API returned status {response.status}")
return None
except asyncio.TimeoutError:
logging.error("Timeout fetching lyrics")
return None
except Exception as e:
logging.error(f"Error fetching lyrics: {str(e)}")
return None
async def fetch_youtube_info(youtube_url):
"""
Asynchronously fetches information about a YouTube video using yt-dlp.
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:
logging.info(f"Fetching YouTube info for: {youtube_url}")
# Configure yt-dlp options
ydl_opts = {
'quiet': True,
'no_warnings': True,
'extract_flat': False,
'skip_download': True,
}
# Run yt-dlp in thread pool to avoid blocking
loop = asyncio.get_event_loop()
def extract_info():
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
return ydl.extract_info(youtube_url, download=False)
info = await loop.run_in_executor(None, extract_info)
if not info:
logging.error("No info returned from yt-dlp")
return None
# Extract video information
title = info.get('title', 'Unknown Title')
description = info.get('description', 'No description available')
duration = info.get('duration', 0)
view_count = info.get('view_count', 0)
uploader = info.get('uploader', 'Unknown')
logging.info(f"Video title: {title}")
length = seconds_to_minutes_seconds(duration)
# Parse artist and song from title
artist, song = get_artist_title(title)
logging.info(f"Parsed artist: {artist}, song: {song}")
# Limit description length to avoid huge messages
if len(description) > 500:
description = description[:500] + "..."
description_with_breaks = description.replace('\n', '<br>')
# Build basic info message
info_message = f"""<strong>🎬🎝 Title:</strong> {title}<br><strong>Length:</strong> {length} | <strong>Views:</strong> {view_count:,} | <strong>Uploader:</strong> {uploader}<br><details><summary><strong>⤵︎Description⤵︎</strong></summary>{description_with_breaks}</details>"""
# Try to fetch lyrics if artist and song were parsed
if artist and song:
logging.info("Attempting to fetch lyrics...")
lyrics = await fetch_lyrics(song, artist)
if lyrics:
lyrics = lyrics.replace('\n', "<br>")
# Limit lyrics length
if len(lyrics) > 3000:
lyrics = lyrics[:3000] + "<br>...(truncated)"
info_message += f"<br><details><summary><strong>🎵 Lyrics:</strong></summary><br>{lyrics}</details>"
else:
logging.info("No lyrics found")
else:
logging.info("Could not parse artist/song from title, skipping lyrics")
return info_message
except Exception as e:
logging.error(f"Error fetching YouTube video information: {str(e)}", exc_info=True)
return None
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)
# Check if message contains a YouTube link
if match.is_not_from_this_bot() and re.search(r'(youtube\.com/watch\?v=|youtu\.be/)', message.body):
logging.info(f"YouTube link detected in message: {message.body}")
# Match both youtube.com and youtu.be formats
video_id_match = re.search(r'(?:youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]{11})', message.body)
if video_id_match:
video_id = video_id_match.group(1)
youtube_url = f"https://www.youtube.com/watch?v={video_id}"
logging.info(f"Fetching information for YouTube video ID: {video_id}")
retry_count = 2 # Reduced retries since yt-dlp is more reliable
while retry_count > 0:
info_message = await fetch_youtube_info(youtube_url)
if info_message:
await bot.api.send_markdown_message(room.room_id, info_message)
logging.info("Sent YouTube video information to the room")
break
else:
logging.warning(f"Failed to fetch info, retrying... ({retry_count-1} attempts left)")
retry_count -= 1
if retry_count > 0:
await asyncio.sleep(2) # wait for 2 seconds before retrying
else:
logging.error("Failed to fetch YouTube video information after all retries")
await bot.api.send_text_message(room.room_id, "Failed to fetch YouTube video information. The video may be unavailable or age-restricted.")
else:
logging.warning("Could not extract video ID from YouTube URL")
+4 -6
View File
@@ -159,16 +159,14 @@ 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=4</li>
<li>--cfg CFG - CFG scale, default=7</li> <li>--cfg CFG - CFG scale, default=2</li>
<li>--h H - Height of the image, default=512</li> <li>--h H - Height of the image, default=512</li>
<li>--w W - Width of the image, default=512</li> <li>--w W - Width of the image, default=512</li>
<li>--neg NEG - Negative prompt, default=none</li> <li>--neg NEG - Negative prompt, default=((((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)))</li>
<li>--sampler SAMPLER - Sampler name, default=Euler a</li> <li>--sampler SAMPLER - Sampler name, default=DPM++ SDE</li>
</ul> </ul>
<p>LORA List:</p> <p>LORA List:</p>
+205
View File
@@ -0,0 +1,205 @@
"""
This plugin provides WHOIS lookup functionality for domains, IPs, and related network information.
"""
import logging
import whois
import ipaddress
import re
import simplematrixbotlib as botlib
def is_valid_domain(domain):
"""
Validate if the provided string is a valid domain name.
Args:
domain (str): The domain to validate.
Returns:
bool: True if valid, False otherwise.
"""
pattern = r'^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|^[a-zA-Z0-9-]{1,63}$'
return re.match(pattern, domain) is not None
def is_valid_ip(ip):
"""
Validate if the provided string is a valid IPv4 or IPv6 address.
Args:
ip (str): The IP address to validate.
Returns:
bool: True if valid, False otherwise.
"""
try:
ipaddress.ip_address(ip)
return True
except ValueError:
return False
def format_whois_data(domain, data):
"""
Format WHOIS data into a readable format.
Args:
domain (str): The queried domain/IP.
data (whois domain object): The WHOIS data object.
Returns:
str: Formatted HTML message.
"""
sections = []
# Domain/Query Information
if hasattr(data, 'domain_name') or hasattr(data, 'query'):
domain_names = getattr(data, 'domain_name', domain)
if isinstance(domain_names, list):
domain_names = ', '.join(domain_names)
sections.append(f"<strong>🔍 Query:</strong> {domain_names}")
# Registrar Information
registrar_items = []
if hasattr(data, 'registrar'):
registrar_items.append(f"<strong>Registrar:</strong> {data.registrar}")
if hasattr(data, 'whois_server'):
registrar_items.append(f"<strong>WHOIS Server:</strong> {data.whois_server}")
if registrar_items:
sections.append('<br>'.join(registrar_items))
# Dates
date_items = []
if hasattr(data, 'creation_date'):
creation = data.creation_date
if isinstance(creation, list):
creation = creation[0]
date_items.append(f"<strong>Created:</strong> {creation}")
if hasattr(data, 'updated_date'):
updated = data.updated_date
if isinstance(updated, list):
updated = updated[0]
date_items.append(f"<strong>Updated:</strong> {updated}")
if hasattr(data, 'expiration_date'):
expiration = data.expiration_date
if isinstance(expiration, list):
expiration = expiration[0]
date_items.append(f"<strong>Expires:</strong> {expiration}")
if date_items:
sections.append('<br>'.join(date_items))
# Status
if hasattr(data, 'status'):
status = data.status
if isinstance(status, list):
status = '<br>'.join(status[:3]) # Limit to first 3 status entries
sections.append(f"<strong>Status:</strong><br>{status}")
# Name Servers
if hasattr(data, 'name_servers'):
name_servers = data.name_servers
if isinstance(name_servers, list):
if len(name_servers) > 5:
name_servers_list = '<br>'.join(sorted(name_servers)[:5])
name_servers_list += f"<br><em>...(+{len(name_servers) - 5} more)</em>"
else:
name_servers_list = '<br>'.join(sorted(name_servers))
else:
name_servers_list = str(name_servers)
sections.append(f"<strong>Name Servers:</strong><br>{name_servers_list}")
# Contact Information
contact_items = []
if hasattr(data, 'org'):
contact_items.append(f"<strong>Organization:</strong> {data.org}")
if hasattr(data, 'country'):
contact_items.append(f"<strong>Country:</strong> {data.country}")
if hasattr(data, 'state'):
contact_items.append(f"<strong>State:</strong> {data.state}")
if hasattr(data, 'city'):
contact_items.append(f"<strong>City:</strong> {data.city}")
if contact_items:
sections.append('<br>'.join(contact_items))
# Build the final message
if sections:
content = f"<strong>🌐 WHOIS Report: {domain}</strong><br><br>"
content += '<br><br>'.join(sections)
else:
content = f"<strong>🌐 WHOIS Information for {domain}</strong><br><br>"
content += "<em>No detailed information available or query returned minimal data.</em>"
# Wrap in collapsible details block for Matrix compatibility
message = f"<details><summary><strong>🌐 WHOIS Report: {domain} (Click to expand)</strong></summary>{content}</details>"
return message
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle the !whois 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.
config (dict): Configuration parameters.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("whois"):
args = match.args()
if len(args) < 1:
await bot.api.send_text_message(
room.room_id,
"Usage: !whois <domain/ip>\nExample: !whois example.com\nExample: !whois 8.8.8.8"
)
return
query = args[0].strip()
logging.info(f"Received !whois command for: {query}")
# Validate the query
if not is_valid_domain(query) and not is_valid_ip(query):
await bot.api.send_text_message(
room.room_id,
f"Invalid domain or IP address format: {query}\nPlease provide a valid domain (e.g., example.com) or IP address."
)
logging.warning(f"Invalid WHOIS query format: {query}")
return
try:
# Perform WHOIS lookup
logging.info(f"Performing WHOIS lookup for: {query}")
await bot.api.send_text_message(room.room_id, f"🔍 Performing WHOIS lookup for {query}...")
# Use python-whois library
whois_data = whois.whois(query)
# Format and send the results
result_message = format_whois_data(query, whois_data)
await bot.api.send_markdown_message(room.room_id, result_message)
logging.info(f"Successfully sent WHOIS results for {query}")
except whois.parser.PywhoisError as e:
error_msg = f"WHOIS lookup failed for {query}.\n"
error_msg += "Possible reasons:\n- Domain/IP not found\n- WHOIS server unavailable\n- Rate limited by registrar"
await bot.api.send_text_message(room.room_id, error_msg)
logging.error(f"WHOIS lookup error for {query}: {e}")
except Exception as e:
await bot.api.send_text_message(
room.room_id,
f"An unexpected error occurred during WHOIS lookup for {query}. Please try again later."
)
logging.error(f"Unexpected error in WHOIS plugin for {query}: {e}", exc_info=True)