Compare commits

...

16 Commits

Author SHA1 Message Date
911f8dd47a Update patch file 2024-02-14 12:00:26 +00:00
Hash Borgir
b10178452b ai plugins cleaned and collapsed output 2024-02-14 03:38:19 -07:00
Hash Borgir
5f5c9d2a0c yt added. Misc output clean up, collapsible text 2024-02-14 02:56:11 -07:00
Hash Borgir
bb331092d0 Config refactor. Plugins now using config 2024-02-14 00:12:40 -07:00
Hash Borgir
2277dd1b90 Testing configuration changes/updates 2024-02-13 22:39:17 -07:00
Hash Borgir
aa7e76e653 README updated 2024-02-13 17:01:58 -07:00
Hash Borgir
976f806397 installer and systemd service added 2024-02-13 16:39:09 -07:00
Hash Borgir
8c23deb13b simplematrixbotlib updated to 2.10 from codeberg 2024-02-13 10:08:26 -07:00
Hash Borgir
df87d39e82 Music plugin updated 2024-02-12 21:31:13 -07:00
Hash Borgir
f50fff0ba6 Plugin for music bot added 2024-02-12 21:23:46 -07:00
Hash Borgir
a459c1299d Plugin for music bot added 2024-02-12 21:23:24 -07:00
Hash Borgir
33ad735292 llm trigger to funguy 2024-02-12 21:12:03 -07:00
Hash Borgir
700dc8ccb6 Updated documenation 2024-02-12 21:01:45 -07:00
Hash Borgir
c9fd5b3d9b Updated documenation 2024-02-12 21:00:59 -07:00
Hash Borgir
ef7704c0bb LLM plugin updated 2024-02-12 20:44:49 -07:00
Hash Borgir
2b7e7db27b LLM plugin added 2024-02-12 20:19:03 -07:00
23 changed files with 820 additions and 201 deletions

5
.gitignore vendored
View File

@@ -6,4 +6,7 @@ session.txt
socks5.txt socks5.txt
venv/ venv/
plugins/__pycache__/ plugins/__pycache__/
simplematrixbotlib/ simplematrixbotlib*/
chromedriver
store
funguybot.service

View File

@@ -12,24 +12,31 @@ Matrix Bot is a Python-based chat bot designed to work with Matrix, an open netw
- Extensible: Users can add new commands by creating additional plugin modules. - Extensible: Users can add new commands by creating additional plugin modules.
## Automatic Installation ## Automatic Installation
1. `./install.sh` or `bash install.sh` Run the installation script
1. `./install-funguy.sh`
2. Set up environment variables: 2. Launch the bot:
Put your bot homeserver/user/pass in `.env` file `sudo systemctl start funguybot`
3. Launch the bot:
`python funguy.py`, or `chmod +x funguy.py`, then `./funguy.py`
## Manual Installation ## Manual Installation
1. Clone the repository: 1. Create python venv
`python3 -m venv venv`
`source venv/bin/activate`
2. Clone the repository:
`git clone https://git.stoned.io/hash/FunguyBot` `git clone https://git.stoned.io/hash/FunguyBot`
2. Install dependencies: 3. Apply the patch
`pip install -r requirements.txt` `cp api.py.patch simplematrixbotlib`
`git apply api.py.patch`
4. Install dependencies:
`cd simplematrixbotlib && pip install .`
`cd ../ && pip install -r requirements.txt`
3. Set up environment variables: 3. Set up environment variables:
Create a `.env` file in the root directory of the bot and add the following variables: Create/Edit `.env` file in the root directory of the bot and add the following variables:
``` ```
MATRIX_URL="https://matrix.org" (or another homeserver) MATRIX_URL="https://matrix.org" (or another homeserver)
@@ -37,9 +44,37 @@ MATRIX_USER=""
MATRIX_PASS="" MATRIX_PASS=""
``` ```
4. Run the bot: 4. Create systemd.service
`python funguy.py`, or `chmod +x funguy.py`, then `./funguy.py` Create `/etc/systemd/system/funguybot.service`
Replace `$working_directory` with your bot install path
```
[Unit]
Description=Funguy Bot Service
After=network.target
[Service]
Type=simple
User=$user
Group=$group
WorkingDirectory=$working_directory
ExecStart=$working_directory/start-funguy.sh
Restart=on-failure
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=funguybot
[Install]
WantedBy=multi-user.target
EOF
```
5. Launch Fungy
```
systemctl daemon-reload
systemctl enable funguybot
systemctl start funguybot
```
## Usage ## Usage
@@ -50,6 +85,8 @@ To use the bot, invite it to a Matrix room and interact with it by sending comma
- `!proxy`: Retrieve and test random SOCKS5 and HTTP proxies. - `!proxy`: Retrieve and test random SOCKS5 and HTTP proxies.
- `!isup <domain/ip>`: Check if the specified domain or IP address is reachable. - `!isup <domain/ip>`: Check if the specified domain or IP address is reachable.
- `!karma <user>`: View or modify karma points for a user. - `!karma <user>`: View or modify karma points for a user.
- `!funguy <prompt>` Talk to the Tech AI LLM
- `!music <prompt>` Talk to the music knowledge LLM
For a complete list of available commands and their descriptions, use the `!commands` command. For a complete list of available commands and their descriptions, use the `!commands` command.
@@ -83,6 +120,13 @@ Increases the karma points for the specified user by 1 in the database and sends
Decreases the karma points for the specified user by 1. 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. 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.
📄 **!funguy [prompt]**
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: **TheBloke_Mistral-7B-Instruct-v0.2-GPTQ**
🎝 **!music [prompt]**
Your music expert! Try it out.
# 🌟 Funguy Bot Credits 🌟 # 🌟 Funguy Bot Credits 🌟
🧙‍♂️ Creator & Developer: 🧙‍♂️ Creator & Developer:

View File

@@ -1,17 +1,17 @@
From 4aa5b913f75e32f6ed06fc6e691daa856824b26a Mon Sep 17 00:00:00 2001 From 7b3421cf893ef8ea36978ae1343f7c8d5d353412 Mon Sep 17 00:00:00 2001
From: Hash Borgir <atirjavid@gmail.com> From: Hash Borgir <hash@stoned.io>
Date: Mon, 12 Feb 2024 19:12:05 -0700 Date: Tue, 13 Feb 2024 15:48:35 -0700
Subject: [PATCH] Fixed stability issue in api.py Subject: [PATCH] api.py patch
--- ---
simplematrixbotlib/api.py | 2 ++ simplematrixbotlib/api.py | 2 ++
1 file changed, 2 insertions(+) 1 file changed, 2 insertions(+)
diff --git a/simplematrixbotlib/api.py b/simplematrixbotlib/api.py diff --git a/simplematrixbotlib/api.py b/simplematrixbotlib/api.py
index 1d87b3d..13a78cf 100644 index 6d51b38..3af7e7e 100644
--- a/simplematrixbotlib/api.py --- a/simplematrixbotlib/api.py
+++ b/simplematrixbotlib/api.py +++ b/simplematrixbotlib/api.py
@@ -295,6 +295,7 @@ class Api: @@ -347,6 +347,7 @@ class Api:
pass # Successful upload pass # Successful upload
else: else:
print(f"Failed Upload Response: {resp}") print(f"Failed Upload Response: {resp}")
@@ -19,7 +19,7 @@ index 1d87b3d..13a78cf 100644
content = { content = {
"body": os.path.basename(image_filepath), "body": os.path.basename(image_filepath),
@@ -342,6 +343,7 @@ class Api: @@ -394,6 +395,7 @@ class Api:
pass # Successful upload pass # Successful upload
else: else:
print(f"Failed Upload Response: {resp}") print(f"Failed Upload Response: {resp}")

12
funguy.conf Normal file
View File

@@ -0,0 +1,12 @@
[simplematrixbotlib.config]
timeout = 65536
join_on_invite = true
encryption_enabled = false
emoji_verify = false
ignore_unverified_devices = true
store_path = "./store/"
allowlist = []
blocklist = []
admin_user = "@hashborgir:mozilla.org"
prefix = "!"
config_file = "funguy.conf"

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# bot.py # funguy.py
import os import os
import logging import logging
@@ -7,26 +7,13 @@ import importlib
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
from dotenv import load_dotenv from dotenv import load_dotenv
import time import time
import sys
# Load environment variables from .env file from plugins.config import FunguyConfig
load_dotenv()
# Bot configuration settings
MATRIX_URL = os.getenv("MATRIX_URL")
MATRIX_USER = os.getenv("MATRIX_USER")
MATRIX_PASS = os.getenv("MATRIX_PASS")
ADMIN_USER = "@hashborgir:mozilla.org"
PREFIX = '!'
creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASS)
bot = botlib.Bot(creds)
# Setup logging # Setup logging
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO) logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)
# Load plugins # Load plugins (defined before plugin load/reload functions)
PLUGINS_DIR = "plugins" PLUGINS_DIR = "plugins"
PLUGINS = {} PLUGINS = {}
@@ -44,21 +31,57 @@ def load_plugins():
def reload_plugins(): def reload_plugins():
global PLUGINS global PLUGINS
PLUGINS = {} PLUGINS = {}
# Unload modules from sys.modules
for plugin_name in list(sys.modules.keys()):
if plugin_name.startswith(PLUGINS_DIR + "."):
del sys.modules[plugin_name]
load_plugins() load_plugins()
def rehash_config(config):
del config
config = FunguyConfig()
# Load environment variables from .env file
load_dotenv()
# Load plugins
load_plugins() load_plugins()
# Bot configuration settings
MATRIX_URL = os.getenv("MATRIX_URL")
MATRIX_USER = os.getenv("MATRIX_USER")
MATRIX_PASS = os.getenv("MATRIX_PASS")
# Get credentials from env
creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASS)
# Bot configuration
config = FunguyConfig()
bot = botlib.Bot(creds, config)
@bot.listener.on_message_event @bot.listener.on_message_event
async def handle_commands(room, message): async def handle_commands(room, message):
match = botlib.MessageMatch(room, message, bot, PREFIX) match = botlib.MessageMatch(room, message, bot, config.prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("reload"): if match.is_not_from_this_bot() and match.prefix() and match.command("reload"):
if str(message.sender) == ADMIN_USER: if str(message.sender) == config.admin_user:
reload_plugins() reload_plugins()
await bot.api.send_text_message(room.room_id, "Plugins reloaded successfully") await bot.api.send_text_message(room.room_id, "Plugins reloaded successfully")
else: else:
await bot.api.send_text_message(room.room_id, "You are not authorized to reload plugins.") await bot.api.send_text_message(room.room_id, "You are not authorized to reload plugins.")
for plugin_name, plugin_module in PLUGINS.items(): for plugin_name, plugin_module in PLUGINS.items():
await plugin_module.handle_command(room, message, bot, PREFIX) await plugin_module.handle_command(room, message, bot, config.prefix, config)
if match.is_not_from_this_bot() and match.prefix() and match.command("rehash"):
if str(message.sender) == config.admin_user:
rehash_config(config)
await bot.api.send_text_message(room.room_id, "Config rehashed")
else:
await bot.api.send_text_message(room.room_id, "You are not authorized to reload plugins.")
bot.run() bot.run()

145
install-funguy.sh Executable file
View File

@@ -0,0 +1,145 @@
#!/bin/bash
# Function to handle errors
handle_error() {
echo "Error: $1"
exit 1
}
# Function to handle sudo errors
handle_sudo_error() {
echo -e "\e[31mError: Failed to execute sudo command: $1\e[0m"
exit 1
}
# Function to execute sudo commands with error handling
sudo_execute() {
echo -e "\e[31m$ sudo $@\e[0m"
sudo "$@" || handle_sudo_error "$*"
}
# Function to create .env file with provided content
create_env() {
echo -e "MATRIX_URL = \"$1\"\nMATRIX_USER = \"$2\"\nMATRIX_PASS = \"$3\"" > .env || handle_error "Failed to create .env file"
}
# Function to prompt user for input
prompt_user() {
read -p "$1: " input
echo "$input"
}
# Store current directory
current_dir=$(pwd)
# Setup python virtual environment
python3 -m venv venv
source venv/bin/activate
# Check if simplematrixbotlib directory already exists
if [ -d "simplematrixbotlib" ] && [ "$(ls -A simplematrixbotlib)" ]; then
echo "simplematrixbotlib directory already exists."
else
# Clone the simplematrixbotlib repository
git clone https://codeberg.org/imbev/simplematrixbotlib.git || handle_error "Failed to clone simplematrixbotlib repository"
fi
# Copy the patch file to the simplematrixbotlib directory
cp api.py.patch "$current_dir/simplematrixbotlib/" || handle_error "Failed to copy patch file"
# Change directory to simplematrixbotlib
cd "$current_dir/simplematrixbotlib/" || handle_error "Failed to change directory to simplematrixbotlib"
# Apply the patch
git apply api.py.patch || handle_error "Failed to apply patch"
# Install simplematrixbotlib
pip install . || handle_error "Failed to install simplematrixbotlib"
# Change back to the original directory
cd "$current_dir"
# Install requirements
pip install -r requirements.txt || handle_error "Failed to install requirements"
# Prompt for Matrix homeserver
read -p "Do you want to use the default homeserver https://matrix.org? (Y/n): " use_default_hs
if [[ $use_default_hs =~ ^[Nn]$ ]]; then
homeserver=$(prompt_user "Enter Matrix homeserver URL (e.g., example.com): ")
homeserver="https://$homeserver"
else
homeserver="https://matrix.org"
fi
# Prompt for username
username=$(prompt_user "Enter Matrix username: ")
# Prompt for password
password=$(prompt_user "Enter Matrix password: ")
# Create .env file
create_env "$homeserver" "$username" "$password"
# Print ASCII art
cat << "EOF"
_____ ____ _
| ___| _ _ __ __ _ _ _ _ _ | __ ) ___ | |_
| |_ | | | | '_ \ / _\` | | | | | | | | _ \ / _ \| __|
| _|| |_| | | | | (_| | |_| | |_| | | |_) | (_) | |_
|_| \__,_|_| |_|\__, |\__,_|\__, | |____/ \___/ \__|
|___/ |___/
By HB (@hashborgir@mozilla.org)
--------------------------------------------------------
EOF
# Echo setup completion message
echo "Setting up funguy bot..."
echo "Setting up systemd service..."
working_directory=$current_dir
# Prompt for user and group
user=$(prompt_user "Enter user")
same_group=$(prompt_user "Is group same as user? (Y/n): ")
if [[ $same_group =~ ^[Yy]$ ]]; then
group=$user
else
group=$(prompt_user "Enter group")
fi
# Create systemd service file
cat <<EOF > funguybot.service
[Unit]
Description=Funguy Bot Service
After=network.target
[Service]
Type=simple
User=$user
Group=$group
WorkingDirectory=$working_directory
ExecStart=$working_directory/start-funguy.sh
Restart=on-failure
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=funguybot
[Install]
WantedBy=multi-user.target
EOF
# Copy systemd service file
sudo_execute cp funguybot.service /etc/systemd/system/ || handle_sudo_error "Failed to copy systemd service file"
# Enable and start the service
sudo_execute systemctl daemon-reload || handle_sudo_error "Failed to reload systemd daemon"
sudo_execute systemctl enable funguybot || handle_sudo_error "Failed to enable funguybot service"
sudo_execute systemctl start funguybot || handle_sudo_error "Failed to start funguybot service"
echo "Funguy bot has been successfully installed and the service has been enabled."
echo "Before starting the service, make sure to edit the .env file and add your bot account's homeserver URL, username, and password."
echo ""
echo "Once done, start the service by running the following command:"
echo ""
echo ""
echo "sudo systemctl start funguybot"

View File

@@ -1,51 +0,0 @@
#!/bin/bash
# Store current directory
current_dir=$(pwd)
# Clone the simplematrixbotlib repository
git clone https://github.com/imbev/simplematrixbotlib.git
# Copy the patch file to the simplematrixbotlib directory
cp 0001-Fixed-stability-issue-in-api.py.patch $current_dir/simplematrixbotlib/
# Change directory to simplematrixbotlib
cd $current_dir/simplematrixbotlib/
# Apply the patch
git apply 0001-Fixed-stability-issue-in-api.py.patch
# Remove the patch file
rm 0001-Fixed-stability-issue-in-api.py.patch
# Install simplematrixbotlib
pip install .
# Change back to the original directory
cd $current_dir
# Install requirements
pip install -r requirements.txt
# Create .env file with required content
echo -e "MATRIX_URL = \"https://matrix.org\"\nMATRIX_USER = \"\"\nMATRIX_PASS = \"\"" > .env
# Print ASCII art
echo " _____ ____ _ "
echo "| ___| _ _ __ __ _ _ _ _ _ | __ ) ___ | |_ "
echo "| |_ | | | | '_ \ / _\` | | | | | | | | _ \ / _ \| __| "
echo "| _|| |_| | | | | (_| | |_| | |_| | | |_) | (_) | |_ "
echo "|_| \__,_|_| |_|\__, |\__,_|\__, | |____/ \___/ \__| "
echo " |___/ |___/ "
# Echo setup completion message
echo "Bot setup completed."
# Prompt user to modify funguy.py to set ADMIN_USER
echo "Modify .env file and set your credentials and homeserver"
echo "Please open funguy.py and set the ADMIN_USER variable to your admin username."
# Launch the bot
echo "Launch the bot with 'python funguy.py'"

69
plugins/ai-music.py Normal file
View File

@@ -0,0 +1,69 @@
"""
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
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']
if new_text.count('\n') > 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}")

70
plugins/ai-tech.py Normal file
View File

@@ -0,0 +1,70 @@
"""
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
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']
if new_text.count('\n') > 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}")

View File

@@ -1,61 +0,0 @@
# plugins/commands.py
import logging
import simplematrixbotlib as botlib
async def handle_command(room, message, bot, PREFIX):
"""
Function to handle the !commands command.
Args:
room (Room): The Matrix room where the command was invoked.
message (RoomMessage): The message object containing the command.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, PREFIX)
if match.is_not_from_this_bot() and match.prefix() and match.command("commands"):
logging.info("Fetching commands documentation")
commands_message = """
# 🍄 Funguy Bot Commands 🍄
🃏 **!fortune**
Returns a random fortune message.
Executes the `/usr/games/fortune` utility and sends the output as a message to the chat room.
⏰ **!date**
Displays the current date and time.
Fetches the current date and time using Python's `datetime` module and sends it in a formatted message to the chat room.
💻 **!proxy**
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.
📶 **!isup <domain/ip>**
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.
☯ **!karma <user>**
Retrieves the karma points for the specified user.
Retrieves the karma points for the specified user from a database and sends them as a message to the chat room.
⇧ **!karma <user> up**
Increases the karma points for the specified user by 1.
Increases the karma points for the specified user by 1 in the database and sends the updated points as a message to the chat room.
⇩ **!karma <user> down**
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.
# 🌟 Funguy Bot Credits 🌟
🧙‍♂️ Creator & Developer:
- Hash Borgir is the author of 🍄Funguy Bot🍄. (@hashborgir:mozilla.org)
# Join our Matrix Room:
[Self-hosting | Security | Sysadmin | Homelab | Programming](https://chat.mozilla.org/#/room/#selfhosting:mozilla.org)
"""
await bot.api.send_markdown_message(room.room_id, commands_message)
logging.info("Sent commands documentation to the room")

144
plugins/config.py Normal file
View File

@@ -0,0 +1,144 @@
# plugins/config.py
import os
import logging
import simplematrixbotlib as botlib
from dataclasses import dataclass
@dataclass
class FunguyConfig(botlib.Config):
"""
Custom configuration class for the Funguy bot.
Extends the base Config class to provide additional configuration options.
Args:
config_file (str): Path to the configuration file.
"""
def __init__(self, config_file="funguy.conf"):
super().__init__()
# Load configuration from file
self.load_toml(config_file)
logging.info(f"Loaded configuration from {config_file}")
_admin_user: str = ""
_prefix: str = ""
_config_file: str= ""
# Define getters and setters for custom configuration options
@property
def admin_user(self):
return self._admin_user
@admin_user.setter
def admin_user(self, value):
self._admin_user = value
@property
def prefix(self):
return self._prefix
@prefix.setter
def prefix(self, value):
self._prefix = value
@property
def config_file(self):
return self._config_file
@config_file.setter
def config_file(self, value):
self._config_file = value
# Method to load configuration from file
def load_config(self, config_file):
"""
Load configuration options from a TOML file.
Args:
config_file (str): Path to the configuration file.
Returns:
None
"""
self.load_toml(config_file)
logging.info(f"Loaded configuration from {config_file}")
# Method to save configuration to file
def save_config(self, config_file):
"""
Save configuration options to a TOML file.
Args:
config_file (str): Path to the configuration file.
Returns:
None
"""
self.save_toml(config_file)
logging.info(f"Saved configuration to {config_file}")
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle commands related to bot configuration.
Args:
room (Room): The Matrix room where the command was invoked.
message (RoomMessage): The message object containing the command.
bot (Bot): The bot instance.
PREFIX (str): The bot command prefix.
config (FunguyConfig): The bot configuration instance.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("set"):
args = match.args()
if len(args) != 2:
await bot.api.send_text_message(room.room_id, "Usage: !set <config_option> <value>")
return
option, value = args
if option == "admin_user":
config.admin_user = value
await bot.api.send_text_message(room.room_id, f"Admin user set to {value}")
elif option == "prefix":
config.prefix = value
await bot.api.send_text_message(room.room_id, f"Prefix set to {value}")
else:
await bot.api.send_text_message(room.room_id, "Invalid configuration option")
elif match.is_not_from_this_bot() and match.prefix() and match.command("get"):
args = match.args()
if len(args) != 1:
await bot.api.send_text_message(room.room_id, "Usage: !get <config_option>")
return
option = args[0]
if option == "admin_user":
await bot.api.send_text_message(room.room_id, f"Admin user: {config.admin_user}")
elif option == "prefix":
await bot.api.send_text_message(room.room_id, f"Prefix: {config.prefix}")
else:
await bot.api.send_text_message(room.room_id, "Invalid configuration option")
elif match.is_not_from_this_bot() and match.prefix() and match.command("saveconf"):
config.save_config(config.config_file)
await bot.api.send_text_message(room.room_id, "Configuration saved")
elif match.is_not_from_this_bot() and match.prefix() and match.command("loadconf"):
config.load_config(config.config_file)
await bot.api.send_text_message(room.room_id, "Configuration loaded")
elif match.is_not_from_this_bot() and match.prefix() and match.command("show"):
admin_user = config.admin_user
prefix = config.prefix
await bot.api.send_text_message(room.room_id, f"Admin user: {admin_user}, Prefix: {prefix}")
elif match.is_not_from_this_bot() and match.prefix() and match.command("reset"):
config.admin_user = ""
config.prefix = "!"
await bot.api.send_text_message(room.room_id, "Configuration reset")

View File

@@ -1,10 +1,14 @@
"""
This plugin provides a command to get the current date
"""
# plugins/date.py # plugins/date.py
import datetime import datetime
import logging import logging
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
async def handle_command(room, message, bot, PREFIX): async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle the !date command. Function to handle the !date command.
@@ -15,7 +19,7 @@ async def handle_command(room, message, bot, PREFIX):
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("date"): if match.is_not_from_this_bot() and match.prefix() and match.command("date"):
logging.info("Fetching current date and time") logging.info("Fetching current date and time")
current_datetime = datetime.datetime.now() current_datetime = datetime.datetime.now()

View File

@@ -1,10 +1,13 @@
"""
This plugin provides a command to get a random fortune message.
"""
# plugins/fortune.py # plugins/fortune.py
import subprocess import subprocess
import logging import logging
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
async def handle_command(room, message, bot, PREFIX): async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle the !fortune command. Function to handle the !fortune command.
@@ -15,7 +18,7 @@ async def handle_command(room, message, bot, PREFIX):
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("fortune"): if match.is_not_from_this_bot() and match.prefix() and match.command("fortune"):
logging.info("Received !fortune command") logging.info("Received !fortune command")
fortune_output = "🃏 " + subprocess.run(['/usr/games/fortune'], capture_output=True).stdout.decode('UTF-8') fortune_output = "🃏 " + subprocess.run(['/usr/games/fortune'], capture_output=True).stdout.decode('UTF-8')

71
plugins/help.py Normal file
View File

@@ -0,0 +1,71 @@
"""
This plugin provides a command to display the list of available commands and their descriptions.
"""
# plugins/help.py
import logging
import simplematrixbotlib as botlib
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle the !help command.
Args:
room (Room): The Matrix room where the command was invoked.
message (RoomMessage): The message object containing the command.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("help"):
logging.info("Fetching command help documentation")
commands_message = """<details><summary><strong>🍄Funguy Bot Commands🍄<br>⤵Click Here To See Help Text⤵</strong></summary>
<br>
<br>🃏 **!fortune**
<br>Returns a random fortune message.
<br>Executes the `/usr/games/fortune` utility and sends the output as a message to the chat room.
<br>
<br>⏰ **!date**
<br>Displays the current date and time.
<br>Fetches the current date and time using Python's `datetime` module and sends it in a formatted message to the chat room.
<br>
<br>💻 **!proxy**
<br>Retrieves a tested/working random SOCKS5 proxy.
<br>Fetches a list of SOCKS5 proxies, tests their availability, and sends the first working proxy to the chat room.
<br>
<br>📶 **!isup [domain/ip]**
<br>Checks if the specified domain or IP address is reachable.
<br>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.
<br>
<br>☯ **!karma [user]**
<br>Retrieves the karma points for the specified user.
<br>Retrieves the karma points for the specified user from a database and sends them as a message to the chat room.
<br>
<br>⇧ **!karma [user] up**
<br>Increases the karma points for the specified user by 1.
<br>Increases the karma points for the specified user by 1 in the database and sends the updated points as a message to the chat room.
<br>
<br>⇩ **!karma [user] down**
<br>Decreases the karma points for the specified user by 1.
<br>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.
<br>
<br>📄 **!funguy [prompt]
<br>An AI large language model designed to serve as a chatbot within a vibrant and diverse community chat room hosted on the Matrix platform.
<br>(Currently loaded model: **TheBloke_Mistral-7B-Instruct-v0.2-GPTQ**
<br>
<br>🎝 **!music [prompt]**
<br>Your music expert! Try it out.
<br> 🌟 Funguy Bot Credits 🌟
<br>
<br>🧙‍♂️ Creator & Developer:
<br> Hash Borgir is the author of 🍄Funguy Bot🍄. (@hashborgir:mozilla.org)
<br>
<br> Join our Matrix Room:
<br>
<br>[Self-hosting | Security | Sysadmin | Homelab | Programming](https://matrix.to/#/#selfhosting:mozilla.org)
<br>
<br></details>"""
await bot.api.send_markdown_message(room.room_id, commands_message)
logging.info("Sent help documentation to the room")

View File

@@ -1,3 +1,7 @@
"""
This plugin provides a command to check if a website or server is up.
"""
# plugins/isup.py # plugins/isup.py
import logging import logging
@@ -39,7 +43,7 @@ async def check_https(domain):
except aiohttp.ClientError: except aiohttp.ClientError:
return False return False
async def handle_command(room, message, bot, PREFIX): async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle the !isup command. Function to handle the !isup command.
@@ -50,7 +54,7 @@ async def handle_command(room, message, bot, PREFIX):
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("isup"): if match.is_not_from_this_bot() and match.prefix() and match.command("isup"):
logging.info("Received !isup command") logging.info("Received !isup command")
args = match.args() args = match.args()

View File

@@ -1,10 +1,15 @@
"""
This plugin provides a command to manage karma points for users.
"""
# plugins/karma.py # plugins/karma.py
import sqlite3 import sqlite3
import logging import logging
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
async def handle_command(room, message, bot, PREFIX): async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle the !karma command. Function to handle the !karma command.
@@ -15,7 +20,7 @@ async def handle_command(room, message, bot, PREFIX):
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("karma"): if match.is_not_from_this_bot() and match.prefix() and match.command("karma"):
logging.info("Received !karma command") logging.info("Received !karma command")
args = match.args() args = match.args()

38
plugins/loadplugin.py Normal file
View File

@@ -0,0 +1,38 @@
# plugins/load_plugin.py
import os
import logging
import importlib
import simplematrixbotlib as botlib
PLUGINS = {}
async def load_plugin(plugin_name):
try:
module = importlib.import_module(f"plugins.{plugin_name}")
PLUGINS[plugin_name] = module
logging.info(f"Loaded plugin: {plugin_name}")
return True
except Exception as e:
logging.error(f"Error loading plugin {plugin_name}: {e}")
return False
async def handle_command(room, message, bot, prefix, config):
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("load"):
if str(message.sender) == config.admin_user:
args = match.args()
if len(args) != 1:
await bot.api.send_text_message(room.room_id, "Usage: !load <plugin>")
else:
plugin_name = args[0]
if plugin_name not in PLUGINS:
success = await load_plugin(plugin_name)
if success:
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' loaded successfully")
else:
await bot.api.send_text_message(room.room_id, f"Error loading plugin '{plugin_name}'")
else:
await bot.api.send_text_message(room.room_id, f"Plugin '{plugin_name}' is already loaded")
else:
await bot.api.send_text_message(room.room_id, "You are not authorized to load plugins.")

58
plugins/plugins.py Normal file
View File

@@ -0,0 +1,58 @@
"""
This plugin provides a command to list all loaded plugins along with their descriptions.
"""
# plugins/plugins.py
import os
import sys
import logging
import simplematrixbotlib as botlib
async def handle_command(room, message, bot, prefix, config):
"""
Function to handle the !plugins command.
Args:
room (Room): The Matrix room where the command was invoked.
message (RoomMessage): The message object containing the command.
Returns:
None
"""
match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and match.prefix() and match.command("plugins"):
logging.info("Received !plugins command")
plugin_descriptions = get_plugin_descriptions()
# Prepend custom string before the output
plugin_descriptions.insert(0, "<details><summary><strong>🔌Plugins List🔌<br>⤵Click Here to Expand⤵</strong></summary><br>")
plugins_message = "<br><br>".join(plugin_descriptions)
plugins_message += "</details>"
await bot.api.send_markdown_message(room.room_id, plugins_message)
logging.info("Sent plugin list to the room")
def get_plugin_descriptions():
"""
Function to get descriptions of all loaded plugin modules.
Returns:
list: A list of plugin descriptions sorted alphabetically.
"""
plugin_descriptions = []
for module_name, module in sys.modules.items():
if module_name.startswith("plugins.") and hasattr(module, "__file__"):
plugin_path = module.__file__
plugin_name = os.path.basename(plugin_path).split(".")[0]
try:
description = module.__doc__.strip().split("\n")[0]
except AttributeError:
description = "No description available"
plugin_descriptions.append(f"<strong>[{plugin_name}.py]:</strong> {description}")
# Sort the plugin descriptions alphabetically by plugin name
plugin_descriptions.sort()
return plugin_descriptions

View File

@@ -1,3 +1,7 @@
"""
This plugin provides a command to get random SOCKS5 proxies.
"""
# plugins/proxy.py # plugins/proxy.py
import os import os
@@ -68,7 +72,7 @@ async def download_proxy_list():
return False return False
async def handle_command(room, message, bot, PREFIX): async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle the !proxy command. Function to handle the !proxy command.
@@ -79,7 +83,7 @@ async def handle_command(room, message, bot, PREFIX):
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("proxy"): if match.is_not_from_this_bot() and match.prefix() and match.command("proxy"):
logging.info("Received !proxy command") logging.info("Received !proxy command")

View File

@@ -1,3 +1,7 @@
"""
This plugin provides a command to fetch YouTube video information from links.
"""
# plugins/youtube.py # plugins/youtube.py
import re import re
@@ -5,7 +9,12 @@ import logging
from pytube import YouTube from pytube import YouTube
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
async def handle_command(room, message, bot, PREFIX): def seconds_to_minutes_seconds(seconds):
minutes = seconds // 60
seconds %= 60
return f"{minutes:02d}:{seconds:02d}"
async def handle_command(room, message, bot, prefix, config):
""" """
Function to handle YouTube video information from links. Function to handle YouTube video information from links.
@@ -16,7 +25,7 @@ async def handle_command(room, message, bot, PREFIX):
Returns: Returns:
None None
""" """
match = botlib.MessageMatch(room, message, bot) match = botlib.MessageMatch(room, message, bot, prefix)
if match.is_not_from_this_bot() and re.search(r'youtube\.com/watch\?v=', message.body): 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)
@@ -28,10 +37,10 @@ async def handle_command(room, message, bot, PREFIX):
video = YouTube(youtube_url) video = YouTube(youtube_url)
title = video.title title = video.title
description = video.description description = video.description
length = video.length length = seconds_to_minutes_seconds(video.length)
views = video.views views = video.views
author = video.author author = video.author
info_message = f"""**🎬🎝 Title:** {title} | **Length**: {length} sec | **Views:** {views} | **Description:** {description}""" info_message = f"""**🎬🎝 Title:** {title} | **Length**: {length} minutes| **Views:** {views} | **Description:** {description}"""
await bot.api.send_markdown_message(room.room_id, info_message) await bot.api.send_markdown_message(room.room_id, info_message)
logging.info("Sent YouTube video information to the room") logging.info("Sent YouTube video information to the room")
except Exception as e: except Exception as e:

38
plugins/youtube-search.py Normal file
View File

@@ -0,0 +1,38 @@
# plugins/youtube_search.py
import logging
import simplematrixbotlib as botlib
from youtube_search import YoutubeSearch
async def handle_command(room, message, bot, PREFIX, config):
match = botlib.MessageMatch(room, message, bot, PREFIX)
if match.is_not_from_this_bot() and match.prefix() and match.command("yt"):
args = match.args()
if len(args) < 1:
await bot.api.send_text_message(room.room_id, "Usage: !yt <search terms>")
else:
search_terms = " ".join(args)
logging.info(f"Performing YouTube search for: {search_terms}")
results = YoutubeSearch(search_terms, max_results=3).to_dict()
if results:
output = generate_output(results)
await send_collapsible_message(room, bot, output)
else:
await bot.api.send_text_message(room.room_id, "No results found.")
def generate_output(results):
output = ""
for video in results:
output += f'<a href="https://www.youtube.com/watch?v={video["id"]}">'
output += f'<img src="{video["thumbnails"][0]}"></img><br>'
output += f'<strong>{video["title"]}</strong><br>'
output += f'Length: {video["duration"]} | Views: {video["views"]}<br>'
if video["long_desc"]:
output += f'Description: {video["long_desc"]}<br>'
output += "</a><br>"
return output
async def send_collapsible_message(room, bot, content):
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)

View File

@@ -1,35 +1,5 @@
aiofiles==23.2.1 python-dotenv
aiohttp==3.9.3 requests
aiohttp-socks==0.7.1 pytube
aiosignal==1.3.1 duckduckgo_search
async-timeout==4.0.3 nio
attrs==23.2.0
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
cryptography==42.0.2
frozenlist==1.4.1
h11==0.14.0
h2==4.1.0
hpack==4.0.0
hyperframe==6.0.1
idna==3.6
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
Markdown==3.5.2
matrix-nio==0.23.0
multidict==6.0.5
pillow==10.2.0
pycparser==2.21
pycryptodome==3.20.0
python-cryptography-fernet-wrapper==1.0.4
python-dotenv==1.0.1
python-socks==2.4.4
pytube==15.0.0
referencing==0.33.0
requests==2.31.0
rpds-py==0.17.1
toml==0.10.2
unpaddedbase64==2.1.0
urllib3==2.2.0
yarl==1.9.4

17
start-funguy.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Function to handle errors
handle_error() {
echo "Error: $1"
exit 1
}
# Load Python virtual environment
source venv/bin/activate || { echo "Error: Failed to load Python virtual environment."; exit 1; }
# Get python path
python=$(which python)
# Run funguy.py
$python funguy.py || { echo "Error: Failed to execute funguy.py."; exit 1; }