discordbot / discord_bot.py
joytou's picture
Import fishspeech manually
b5f5b20
# This code is based on the following example:
# https://discordpy.readthedocs.io/en/stable/quickstart.html#a-minimal-bot
import sys
import os
import discord
from discord import app_commands
from discord.ext import commands
from threading import Thread
import json
from horde import HordeAPI
import fishspeech
import inspect
from async_eval import eval
# 读取json
with open("discord.json", "r") as f:
json_data = json.load(f)
# 创建一个字典,将规则类型映射到相应的条件检查函数
check_functions = {
"equals": lambda content, message: message.content == content,
"contains": lambda content, message: content in message.content,
"startswith": lambda content, message: message.content.startswith(content),
"endswith": lambda content, message: message.content.endswith(content)
}
# 定义bot和tree
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(
command_prefix=json_data["command_prefix"] if "command_prefix" in json_data else '>',
intents=intents
)
tree = bot.tree
COMMAND_NAME_PREFIX=json_data["command_name_prefix"] if "command_name_prefix" in json_data else "discord_bot_call_"
def load_module_by_function_name(name: str = ""):
"""
根据函数名称,导入对应的模块
Args:
name: 函数名称
"""
module_name=name.split(".")[0]
if name.find(".") > 0 and module_name not in sys.modules:
exec(f"import {module_name}")
def generate_discord_command_callback_param_str(pre: str = '', parameters: list = []) -> str:
"""
生成discord command的callback的调用参数
Args:
pre: 前置固定参数
parameters: 参数列表
Returns:
string: 生成的调用参数的代码
"""
return f'''
{pre}{''.join([f", {param['name']}: {param['type']}" + (f' = "{param["default"]}"' if 'default' in param and param['type'] == 'str' else f" = {param['default']}" if 'default' in param else '')
for param in parameters])}
'''
def generate_user_function_param_str(parameters: list = []) -> str:
"""
生成调用用户自定义的函数的参数
Args:
parameters: 参数列表
Returns:
str: 生成的调用参数的代码
"""
return f'''{', '.join([f"{param['name']}={param['name']}"
for param in parameters])}'''
def generate_tree_add_command(command: dict = {}) -> str:
"""
生成tree add_command
Args:
command: command
Returns:
str: 生成的调用代码
"""
return f'''
tree.add_command(app_commands.Command(
name="{command['name']}",
description="{command['description']}",
callback={COMMAND_NAME_PREFIX}{command['name']}
))
'''
def generate_discord_command_callback_function_str(command: dict = {}) -> str:
"""
生成discord command的callback的调用函数
Args:
command: command
Returns:
str: 生成的调用代码
"""
return f'''
async def {COMMAND_NAME_PREFIX}{command['name']}({generate_discord_command_callback_param_str('interaction: discord.Interaction', command['parameters'] if 'parameters' in command else [])}):
await interaction.response.defer()
result = await {command['function']}({generate_user_function_param_str(command['parameters'] if 'parameters' in command else [])})
if result is not None:
await interaction.followup.send(result)
'''
def generate_bot_command_send(command: dict = {}) -> str:
"""
生成bot.command的send语句
Args:
command: command
Returns:
str: 生成的调用代码
"""
return f'''await ctx.send(\"{command['response']}\")'''
def generate_bot_command_callback(command: dict = {}) -> str:
"""
生成bot.command的调用用户函数和send语句
Args:
command: command
Returns:
str: 生成的调用代码
"""
return f'''result = await {command['function']}({generate_user_function_param_str(command['parameters'] if 'parameters' in command else [])})
await ctx.send(result)'''
def generate_bot_command_def(command: dict = {}) -> str:
"""
生成bot.command的定义函数
Args:
command: command
Returns:
str: 生成的调用代码
"""
return f'''
@bot.command()
async def {command['name']}({generate_discord_command_callback_param_str('ctx', command['parameters'] if 'parameters' in command else [])}):
{generate_bot_command_send(command) if "response" in command else generate_bot_command_callback(command)}
'''
if "app_command" in json_data:
for command in json_data["app_command"]:
load_module_by_function_name(command["function"])
exec(generate_discord_command_callback_function_str(command))
exec(generate_tree_add_command(command))
if "command" in json_data:
for command in json_data["command"]:
if "function" in command:
load_module_by_function_name(command["function"])
exec(generate_bot_command_def(command))
async def get_help(command_name: str = ''):
def do_with_message(message: dict):
result = ''
result += f'If message is '
match message['type']:
case "equals":
result += ''
case "contains":
result += 'contains '
case "startswith":
result += 'starts with '
case "endswith":
result += 'ends with '
result += f'"{message["content"]}", then this app will '
result += generate_command_help_info(message)
return result
def do_with_command(command: dict):
result = ''
result += f'If message is starts with "{command["name"]}", then this app will '
result += generate_command_help_info(command)
return result
def do_with_app_command(command: dict):
result = ''
result += f'If message is starts with "/{command["name"]}", then this app will '
result += generate_command_help_info(command)
return result
def generate_command_help_info(message: dict):
result = ''
if 'response' in message:
result += f'response "{message["response"]}"'
elif 'function' in message:
if 'description' in message:
result += f'{message["description"][0].lower()}{message["description"][1:]}'
else:
result += f'execulate function which named "{message["function"]}"'
if 'parameters' in message and len(message['parameters']) > 0:
result += ', with parameter(s): '
for param in message['parameters']:
result += '\n\t'
result += f'- {param["name"]}: {param["type"]}'
if 'default' in param:
result += ' = '
match param['type']:
case 'str':
result += f'"{param["default"]}"'
case _:
result += f'"{param["default"]}"'
if 'description' in param:
result += f'\n\t {param["description"]}'
else:
result += 'do nothing'
result += '\n'
return result
result = ''
if command_name is None or command_name == '':
if 'message' in json_data:
for message in json_data['message']:
result += do_with_message(message)
if 'command' in json_data:
for command in json_data['command']:
result += do_with_command(command)
if 'app_command' in json_data:
for command in json_data['app_command']:
result += do_with_app_command(command)
else:
if 'message' in json_data:
for message in json_data['message']:
if message['content'] != command_name:
continue
result += do_with_message(message)
if 'command' in json_data:
for command in json_data['command']:
if command['name'] != command_name:
continue
result += do_with_command(command)
if 'app_command' in json_data:
for command in json_data['app_command']:
if command['name'] != command_name:
continue
result += do_with_app_command(command)
if result is None or result == '':
result = f'Can\'t not find "{command_name}", you can type "/help" to query all commands, or "/help [command name]" to query the command.'
return result
async def greet(name: str):
return f"Hello, {name}!"
async def get_kudos():
async with HordeAPI.getUserDetails() as details:
if "kudos" not in details:
return f'Error: {details["code"]} {details["reason"]}'
return f'The amount of Kudos this user has is {details["kudos"]}'
async def generate_status(id: str):
async with HordeAPI.generateCheck(id) as details:
if "kudos" not in details:
return f'Check Error: {details["code"]} {details["reason"]}'
if bool(details["is_possible"]) is False:
return "This generation is impossible."
if bool(details["faulted"]) is True:
return "This generation is faulted."
if bool(details["done"]) is True:
async with HordeAPI.generateStatus(id) as generation_detail:
if "generations" not in generation_detail:
return f'Status Error: {generation_detail["code"]} {generation_detail["reason"]}'
for i in range(len(generation_detail["generations"])):
return generation_detail["generations"][i]["img"]
if int(details["processing"]) > 0:
total = int(details["finished"])
+ int(details["processing"])
+ int(details["queue_position"])
+ int(details["restarted"])
+ int(details["waiting"])
return f'Processing image: {details["processing"]}/{total}'
return f'Position in queue: {details["queue_position"]}, wait time: {details["wait_time"]}s'
@bot.event
async def on_ready():
await tree.sync()
await bot.tree.sync()
print('We have logged in as {0.user}'.format(bot))
@bot.event
async def on_message(message):
if message.author == bot.user:
return
if "message" in json_data:
for rule in json_data["message"]:
rule_type = rule["type"]
content = rule["content"]
# 根据规则类型动态调用对应的判断函数
if check_functions.get(rule_type, lambda c, m: False)(content, message):
# 如果规则指定了函数,则调用对应的函数
if "function" in rule:
function_name = rule["function"]
load_module_by_function_name(function_name)
result = eval(f"await {function_name}()")
await message.channel.send(result)
# 否则发送预定义的响应消息
elif "response" in rule:
await message.channel.send(rule["response"])
# 确保命令系统正常工作
await bot.process_commands(message)
async def sendMessageToChannelHelper(data):
channel = await bot.fetch_channel(os.environ.get("CHANNEL_ID"))
# 创建一个 embed 对象
mTitle = "Empty Title"
if "id" in data:
mTitle = data["id"]
if "log_tag" in data:
mTitle = data["log_tag"]
mDescription = "Empty Description"
if "model" in data:
mDescription = data["model"]
if "log_message" in data:
mDescription = data["log_message"]
mColor = 0x00ff00
if ("log_tag" in data or "log_message" in data) and (data["log_level"] == "assert" or data["log_level"] == "error"):
mColor = 0xff0000
embed = discord.Embed(title=mTitle, description=mDescription, color=mColor)
# 将 fields 数据加入 embed
for field in data:
if field == "img":
embed.set_image(url=data[field])
else:
embed.add_field(name=field, value=data[field], inline=True)
# 发送 embed 消息
await channel.send(embed=embed)
def sendMessageToChannel(data):
bot.loop.create_task(sendMessageToChannelHelper(data))
def run():
try:
token = json_data["token"] if "token" in json_data else (os.environ.get("TOKEN") or "")
if token == "":
raise Exception("Please add your token to the Secrets pane.")
bot.run(token)
except discord.HTTPException as e:
if e.status == 429:
print(
"The Discord servers denied the connection for making too many requests"
)
print(
"Get help from https://stackoverflow.com/questions/66724687/in-discord-py-how-to-solve-the-error-for-toomanyrequests"
)
else:
raise e
def discord_bot():
print("Running discord_bot")
run()
if __name__ == "__main__":
discord_bot()