Port discord chatbot to abc framework

frontend_abc
Guillermo Rodriguez 2025-02-19 14:45:24 -03:00
parent f5a734a622
commit 239fb59431
No known key found for this signature in database
GPG Key ID: 002CC5F1E6BDA53E
9 changed files with 420 additions and 1517 deletions

View File

@ -197,17 +197,8 @@ def dgpu(
@run.command()
@click.option('--loglevel', '-l', default='INFO', help='logging level')
@click.option(
'--db-host', '-h', default='localhost:5432')
@click.option(
'--db-user', '-u', default='skynet')
@click.option(
'--db-pass', '-u', default='password')
def telegram(
loglevel: str,
db_host: str,
db_user: str,
db_pass: str
):
import asyncio
from skynet.frontend.chatbot.telegram import TelegramChatbot
@ -232,62 +223,26 @@ def telegram(
@run.command()
@click.option('--loglevel', '-l', default='INFO', help='logging level')
@click.option(
'--db-host', '-h', default='localhost:5432')
@click.option(
'--db-user', '-u', default='skynet')
@click.option(
'--db-pass', '-u', default='password')
def discord(
loglevel: str,
db_host: str,
db_user: str,
db_pass: str
):
import asyncio
from .frontend.discord import SkynetDiscordFrontend
from skynet.frontend.chatbot.discord import DiscordChatbot
from skynet.frontend.chatbot.db import FrontendUserDB
logging.basicConfig(level=loglevel)
config = load_skynet_toml()
dc_token = config.discord.dc_token
key = config.discord.key
account = config.discord.account
permission = config.discord.permission
node_url = config.discord.node_url
hyperion_url = config.discord.hyperion_url
ipfs_url = config.discord.ipfs_url
try:
explorer_domain = config.discord.explorer_domain
except ConfigParsingError:
explorer_domain = DEFAULT_EXPLORER_DOMAIN
try:
ipfs_domain = config.discord.ipfs_domain
except ConfigParsingError:
ipfs_domain = DEFAULT_IPFS_DOMAIN
config = load_skynet_toml().discord
async def _async_main():
frontend = SkynetDiscordFrontend(
# dc_token,
account,
permission,
node_url,
hyperion_url,
db_host, db_user, db_pass,
ipfs_url,
key=key,
explorer_domain=explorer_domain,
ipfs_domain=ipfs_domain
)
async with frontend.open():
await frontend.bot.start(dc_token)
async with FrontendUserDB(
config.db_user,
config.db_pass,
config.db_host,
config.db_name
) as db:
bot = DiscordChatbot(config, db)
await bot.run()
asyncio.run(_async_main())

View File

@ -193,6 +193,7 @@ class BaseChatbot(ABC):
inputs: list[BaseFileInput],
submit_tx_hash: str,
worker: str,
result_url: str,
result_img: bytes | None
):
'''
@ -414,7 +415,7 @@ class BaseChatbot(ABC):
logging.warning(f'couldn\'t get ipfs result at {result_link}!')
await self.update_request_status_final(
msg, status_msg, user, body.params, inputs, submit_tx_hash, worker, result_img)
msg, status_msg, user, body.params, inputs, submit_tx_hash, worker, result_link, result_img)
await self.db.increment_generated(user.id)

View File

@ -0,0 +1,406 @@
import logging
from typing import Self, Awaitable
from datetime import datetime, timezone
import discord
from discord import (
User as DCUser,
Member,
Message as DCMessage,
Attachment,
DMChannel
)
from discord.abc import Messageable
from discord.ext import commands
from skynet.config import FrontendConfig
from skynet.types import BodyV0Params
from skynet.constants import VERSION
from skynet.frontend.chatbot import BaseChatbot
from skynet.frontend.chatbot.db import FrontendUserDB
from skynet.frontend.chatbot.types import (
BaseUser,
BaseChatRoom,
BaseFileInput,
BaseCommands,
BaseMessage
)
GROUP_ID = -1
ADMIN_USER_ID = -1
def timestamp_pretty():
return datetime.now(timezone.utc).strftime('%H:%M:%S')
class DiscordUser(BaseUser):
def __init__(self, user: DCUser | Member):
self._user = user
@property
def id(self) -> int:
return self._user.id
@property
def name(self) -> str:
return self._user.name
@property
def is_admin(self) -> bool:
return self.id == ADMIN_USER_ID
class DiscordChatRoom(BaseChatRoom):
def __init__(self, channel: Messageable):
self._channel = channel
@property
def id(self) -> int:
return self._channel.id
@property
def is_private(self) -> bool:
return isinstance(self._channel, DMChannel)
class DiscordFileInput(BaseFileInput):
def __init__(
self,
attachment: Attachment | None = None,
id: int | None = None,
cid: int | None = None
):
self._attachment = attachment
self._id = id
self._cid = cid
self._raw = None
def from_values(id: int, cid: str) -> Self:
return DiscordFileInput(id=id, cid=cid)
@property
def id(self) -> int:
if self._id:
return self._id
return self._attachment.id
@property
def cid(self) -> str:
if self._cid:
return self._cid
raise ValueError
def set_cid(self, cid: str):
self._cid = cid
async def download(self) -> bytes:
self._raw = await self._attachment.read()
return self._raw
class DiscordMessage(BaseMessage):
def __init__(self, cmd: BaseCommands | None, msg: DCMessage):
self._msg = msg
self._cmd = cmd
self._chat = DiscordChatRoom(msg.channel)
self._inputs: list[DiscordFileInput] | None = None
self._author = None
@property
def id(self) -> int:
return self._msg.id
@property
def chat(self) -> DiscordChatRoom:
return self._chat
@property
def text(self) -> str:
# remove command name, slash and first space
return self._msg.contents[len(self._cmd) + 2:]
@property
def author(self) -> DiscordUser:
if self._author:
return self._author
return DiscordUser(self._msg.author)
@property
def command(self) -> str | None:
return self._cmd
@property
def inputs(self) -> list[DiscordFileInput]:
if self._inputs is None:
self._inputs = []
if self._msg.attachments:
self._inputs = [
DiscordFileInput(attachment=a)
for a in self._msg.attachments
]
return self._inputs
def generate_reply_embed(
config: FrontendConfig,
user: DiscordUser,
params: BodyV0Params,
tx_hash: str,
worker: str,
) -> discord.Embed:
embed = discord.Embed(
title='[SKYNET Transaction Explorer]',
url=f'https://{config.explorer_domain}/v2/explore/transaction/{tx_hash}',
color=discord.Color.blue())
prompt = params.prompt
if len(prompt) > 256:
prompt = prompt[:256]
gen_str = f'generated by {user.name}\n'
gen_str += f'performed by {worker}\n'
gen_str += f'reward: {config.reward}\n'
embed.add_field(
name='General Info', value=f'```{gen_str}```', inline=False)
# meta_str = f'__by {user.name}__\n'
# meta_str += f'*performed by {worker}*\n'
# meta_str += f'__**reward: {reward}**__\n'
embed.add_field(name='Prompt', value=f'```{prompt}\n```', inline=False)
# meta_str = f'`prompt:` {prompt}\n'
meta_str = f'seed: {params.seed}\n'
meta_str += f'step: {params.step}\n'
if params.guidance:
meta_str += f'guidance: {params.guidance}\n'
if params.strength:
meta_str += f'strength: {params.strength}\n'
meta_str += f'algo: {params.model}\n'
if params.upscaler:
meta_str += f'upscaler: {params.upscaler}\n'
embed.add_field(name='Parameters', value=f'```{meta_str}```', inline=False)
foot_str = f'Made with Skynet v{VERSION}\n'
foot_str += 'JOIN THE SWARM: https://discord.gg/PAabjJtZAF'
embed.set_footer(text=foot_str)
return embed
def append_command_handler(client: discord.Client, command: str, help_txt: str, fn: Awaitable):
@client.command(name=command, help=help_txt)
async def wrap_msg_and_handle(ctx: commands.Context):
msg = DiscordMessage(cmd=command, msg=ctx.message)
for file in msg.inputs:
await file.download()
await fn(msg)
class DiscordChatbot(BaseChatbot):
def __init__(
self,
config: FrontendConfig,
db: FrontendUserDB,
):
super().__init__(config, db)
intents = discord.Intents(
messages=True,
guilds=True,
typing=True,
members=True,
presences=True,
reactions=True,
message_content=True,
voice_states=True
)
client = discord.Client(
command_prefix='/',
intents=intents
)
@client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
for guild in client.guilds:
for channel in guild.channels:
if channel.name == "skynet":
await channel.send('Skynet bot online') # view=SkynetView(self.bot))
await channel.send(
'Welcome to Skynet\'s Discord Bot,\n\n'
'Skynet operates as a decentralized compute layer, offering a wide array of '
'support for diverse AI paradigms through the use of blockchain technology. '
'Our present focus is image generation, powered by 11 distinct models.\n\n'
'To begin exploring, use the \'/help\' command or directly interact with the'
'provided buttons. Here is an example command to generate an image:\n\n'
'\'/txt2img a big red tractor in a giant field of corn\''
)
print("\n==============")
print("Logged in as")
print(client.user.name)
print(client.user.id)
print("==============")
@client.event
async def on_message(message: DCMessage):
if message.author == client.user:
return
await self.process_commands(message)
@client.event
async def on_command_error(ctx, error):
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send('You missed a required argument, please try again.')
append_command_handler(client, BaseCommands.HELP, 'Responds with help text', self.send_help)
append_command_handler(client, BaseCommands.COOL, 'Display a list of cool prompt words', self.send_cool_words)
append_command_handler(client, BaseCommands.QUEUE, 'Get information on current skynet queue', self.get_queue)
append_command_handler(client, BaseCommands.CONFIG, 'Allows user to configure inference params', self.set_config)
append_command_handler(client, BaseCommands.STATS, 'See user statistics', self.user_stats)
append_command_handler(client, BaseCommands.DONATE, 'See donation information', self.donation_info)
append_command_handler(client, BaseCommands.SAY, 'Admin command to make bot speak', self.say)
append_command_handler(client, BaseCommands.TXT2IMG, 'Generate an image from a prompt', self.handle_request)
append_command_handler(client, BaseCommands.REDO, 'Re-generate image using last prompt', self.handle_request)
self.client = client
self._main_room: DiscordChatRoom | None = None
async def init(self):
dc_channel = await self.client.get_channel(GROUP_ID)
self._main_room = DiscordChatRoom(channel=dc_channel)
logging.info('initialized')
async def run(self):
await self.init()
await self.client.run(self.config.token)
@property
def main_group(self) -> DiscordChatRoom:
return self._main_room
async def new_msg(self, chat: DiscordChatRoom, text: str, **kwargs) -> DiscordMessage:
dc_msg = await chat._channel.send(text, **kwargs)
return DiscordMessage(cmd=None, msg=dc_msg)
async def reply_to(self, msg: DiscordMessage, text: str, **kwargs) -> DiscordMessage:
dc_msg = await msg._msg.reply(content=text, **kwargs)
return DiscordMessage(cmd=None, msg=dc_msg)
async def edit_msg(self, msg: DiscordMessage, text: str, **kwargs):
await msg._msg.edit(content=text, **kwargs)
async def create_status_msg(self, msg: DiscordMessage, init_text: str, force_user: DiscordUser | None = None) -> tuple[BaseUser, BaseMessage, dict]:
# maybe init user
user = msg.author
if force_user:
user = force_user
user_row = await self.db.get_or_create_user(user.id)
# create status msg
embed = discord.Embed(
title='live updates',
description=init_text,
color=discord.Color.blue()
)
status_msg = await self.new_msg(msg.chat, None, embed=embed)
# start tracking of request in db
await self.db.new_user_request(user.id, msg.id, status_msg.id, status=init_text)
return [user, status_msg, user_row]
async def update_status_msg(self, msg: DiscordMessage, text: str):
await self.db.update_user_request_by_sid(msg.id, text)
embed = discord.Embed(
title='live updates',
description=text,
color=discord.Color.blue()
)
await self.edit_msg(msg, None, embed=embed)
async def append_status_msg(self, msg: DiscordMessage, text: str):
request = await self.db.get_user_request_by_sid(msg.id)
await self.update_status_msg(msg, request['status'] + text)
async def update_request_status_timeout(self, status_msg: DiscordMessage):
await self.append_status_msg(
status_msg,
f'\n[{timestamp_pretty()}] **timeout processing request**',
)
async def update_request_status_step_0(self, status_msg: DiscordMessage, user_msg: DiscordMessage):
await self.update_status_msg(
status_msg,
f'processing a \'{status_msg.cmd}\' request by {status_msg.author.name}\n'
f'[{timestamp_pretty()}] *broadcasting transaction to chain...* '
)
async def update_request_status_step_1(self, status_msg: DiscordMessage, tx_result: dict):
enqueue_tx_id = tx_result['transaction_id']
enqueue_tx_link = f'[**Your request on Skynet Explorer**](https://{self.config.explorer_domain}/v2/explore/transaction/{enqueue_tx_id})'
await self.append_status_msg(
status_msg,
'**broadcasted!** \n'
f'{enqueue_tx_link}\n'
f'[{timestamp_pretty()}] *workers are processing request...* '
)
async def update_request_status_step_2(self, status_msg: DiscordMessage, submit_tx_hash: str):
tx_link = f'[**Your result on Skynet Explorer**](https://{self.config.explorer_domain}/v2/explore/transaction/{submit_tx_hash})'
await self.append_status_msg(
status_msg,
'**request processed!**\n'
f'{tx_link}\n'
f'[{timestamp_pretty()}] *trying to download image...*\n '
)
async def update_request_status_final(
self,
og_msg: DiscordMessage,
status_msg: DiscordMessage,
user: DiscordUser,
params: BodyV0Params,
inputs: list[DiscordFileInput],
submit_tx_hash: str,
worker: str,
result_url: str,
result_img: bytes | None
):
embed = generate_reply_embed(
self.config, user, params, submit_tx_hash, worker)
if not result_img:
# result found on chain but failed to fetch img from ipfs
await self.append_status_msg(status_msg, f'[{timestamp_pretty()}] *Couldn\'t get IPFS hosted img [**here**]({result_url})!*')
return
await status_msg._msg.delete()
embed.set_image(url=result_url)
match len(inputs):
case 0:
await self.new_msg(og_msg.chat, None, embed=embed)
case _:
_input = inputs[-1]
dc_file = discord.File(_input._raw, filename=f'image-{og_msg.id}.png')
embed.set_thumbnail(url=f'attachment://image-{og_msg.id}.png')
await self.new_msg(og_msg.chat, None, embed=embed, file=dc_file)

View File

@ -383,6 +383,7 @@ class TelegramChatbot(BaseChatbot):
inputs: list[TelegramFileInput],
submit_tx_hash: str,
worker: str,
result_url: str,
result_img: bytes | None
):
'''

View File

@ -1,322 +0,0 @@
from json import JSONDecodeError
import random
import logging
import asyncio
from decimal import Decimal
from hashlib import sha256
from datetime import datetime
from contextlib import (
ExitStack,
AsyncExitStack,
)
from contextlib import asynccontextmanager as acm
from leap.cleos import CLEOS
from leap.sugar import (
Name,
asset_from_str,
collect_stdout,
)
from leap.hyperion import HyperionAPI
# from telebot.types import InputMediaPhoto
import discord
import requests
import io
from PIL import Image, UnidentifiedImageError
from skynet.db import open_database_connection
from skynet.ipfs import get_ipfs_file, AsyncIPFSHTTP
from skynet.constants import *
from . import *
from .bot import DiscordBot
from .utils import *
from .handlers import create_handler_context
from .ui import SkynetView
class SkynetDiscordFrontend:
def __init__(
self,
# token: str,
account: str,
permission: str,
node_url: str,
hyperion_url: str,
db_host: str,
db_user: str,
db_pass: str,
ipfs_url: str,
remote_ipfs_node: str,
key: str,
explorer_domain: str,
ipfs_domain: str
):
# self.token = token
self.account = account
self.permission = permission
self.node_url = node_url
self.hyperion_url = hyperion_url
self.db_host = db_host
self.db_user = db_user
self.db_pass = db_pass
self.ipfs_url = ipfs_url
self.remote_ipfs_node = remote_ipfs_node
self.key = key
self.explorer_domain = explorer_domain
self.ipfs_domain = ipfs_domain
self.bot = DiscordBot(self)
self.cleos = CLEOS(None, None, url=node_url, remote=node_url)
self.hyperion = HyperionAPI(hyperion_url)
self.ipfs_node = AsyncIPFSHTTP(ipfs_url)
self._exit_stack = ExitStack()
self._async_exit_stack = AsyncExitStack()
async def start(self):
if self.remote_ipfs_node:
await self.ipfs_node.connect(self.remote_ipfs_node)
self.db_call = await self._async_exit_stack.enter_async_context(
open_database_connection(
self.db_user, self.db_pass, self.db_host))
create_handler_context(self)
async def stop(self):
await self._async_exit_stack.aclose()
self._exit_stack.close()
@acm
async def open(self):
await self.start()
yield self
await self.stop()
# maybe do this?
# async def update_status_message(
# self, status_msg, new_text: str, **kwargs
# ):
# await self.db_call(
# 'update_user_request_by_sid', status_msg.id, new_text)
# return await self.bot.edit_message_text(
# new_text,
# chat_id=status_msg.chat.id,
# message_id=status_msg.id,
# **kwargs
# )
# async def append_status_message(
# self, status_msg, add_text: str, **kwargs
# ):
# request = await self.db_call('get_user_request_by_sid', status_msg.id)
# await self.update_status_message(
# status_msg,
# request['status'] + add_text,
# **kwargs
# )
async def work_request(
self,
user,
status_msg,
method: str,
params: dict,
ctx: discord.ext.commands.context.Context | discord.Message,
file_id: str | None = None,
binary_data: str = ''
) -> bool:
send = ctx.channel.send
if params['seed'] == None:
params['seed'] = random.randint(0, 0xFFFFFFFF)
sanitized_params = {}
for key, val in params.items():
if isinstance(val, Decimal):
val = str(val)
sanitized_params[key] = val
body = json.dumps({
'method': 'diffuse',
'params': sanitized_params
})
request_time = datetime.now().isoformat()
await status_msg.delete()
msg_text = f'processing a \'{method}\' request by {user.name}\n[{timestamp_pretty()}] *broadcasting transaction to chain...* '
embed = discord.Embed(
title='live updates',
description=msg_text,
color=discord.Color.blue())
message = await send(embed=embed)
reward = '20.0000 GPU'
res = await self.cleos.a_push_action(
'gpu.scd',
'enqueue',
{
'user': Name(self.account),
'request_body': body,
'binary_data': binary_data,
'reward': asset_from_str(reward),
'min_verification': 1
},
self.account, self.key, permission=self.permission
)
if 'code' in res or 'statusCode' in res:
logging.error(json.dumps(res, indent=4))
await self.bot.channel.send(
status_msg,
'skynet has suffered an internal error trying to fill this request')
return False
enqueue_tx_id = res['transaction_id']
enqueue_tx_link = f'[**Your request on Skynet Explorer**](https://{self.explorer_domain}/v2/explore/transaction/{enqueue_tx_id})'
msg_text += f'**broadcasted!** \n{enqueue_tx_link}\n[{timestamp_pretty()}] *workers are processing request...* '
embed = discord.Embed(
title='live updates',
description=msg_text,
color=discord.Color.blue())
await message.edit(embed=embed)
out = collect_stdout(res)
request_id, nonce = out.split(':')
request_hash = sha256(
(nonce + body + binary_data).encode('utf-8')).hexdigest().upper()
request_id = int(request_id)
logging.info(f'{request_id} enqueued.')
tx_hash = None
ipfs_hash = None
for i in range(60):
try:
submits = await self.hyperion.aget_actions(
account=self.account,
filter='gpu.scd:submit',
sort='desc',
after=request_time
)
actions = [
action
for action in submits['actions']
if action[
'act']['data']['request_hash'] == request_hash
]
if len(actions) > 0:
tx_hash = actions[0]['trx_id']
data = actions[0]['act']['data']
ipfs_hash = data['ipfs_hash']
worker = data['worker']
logging.info('Found matching submit!')
break
except JSONDecodeError:
logging.error(f'network error while getting actions, retry..')
await asyncio.sleep(1)
if not ipfs_hash:
timeout_text = f'\n[{timestamp_pretty()}] **timeout processing request**'
embed = discord.Embed(
title='live updates',
description=timeout_text,
color=discord.Color.blue())
await message.edit(embed=embed)
return False
tx_link = f'[**Your result on Skynet Explorer**](https://{self.explorer_domain}/v2/explore/transaction/{tx_hash})'
msg_text += f'**request processed!**\n{tx_link}\n[{timestamp_pretty()}] *trying to download image...*\n '
embed = discord.Embed(
title='live updates',
description=msg_text,
color=discord.Color.blue())
await message.edit(embed=embed)
# attempt to get the image and send it
results = {}
ipfs_link = f'https://{self.ipfs_domain}/ipfs/{ipfs_hash}'
ipfs_link_legacy = ipfs_link + '/image.png'
async def get_and_set_results(link: str):
res = await get_ipfs_file(link)
logging.info(f'got response from {link}')
if not res or res.status_code != 200:
logging.warning(f'couldn\'t get ipfs binary data at {link}!')
else:
try:
with Image.open(io.BytesIO(res.raw)) as image:
tmp_buf = io.BytesIO()
image.save(tmp_buf, format='PNG')
png_img = tmp_buf.getvalue()
results[link] = png_img
except UnidentifiedImageError:
logging.warning(
f'couldn\'t get ipfs binary data at {link}!')
tasks = [
get_and_set_results(ipfs_link),
get_and_set_results(ipfs_link_legacy)
]
await asyncio.gather(*tasks)
png_img = None
if ipfs_link_legacy in results:
png_img = results[ipfs_link_legacy]
if ipfs_link in results:
png_img = results[ipfs_link]
if not png_img:
logging.error(f'couldn\'t get ipfs hosted image at {ipfs_link}!')
embed.add_field(
name='Error', value=f'couldn\'t get ipfs hosted image [**here**]({ipfs_link})!')
await message.edit(embed=embed, view=SkynetView(self))
return True
# reword this function, may not need caption
caption, embed = generate_reply_caption(
user, params, tx_hash, worker, reward, self.explorer_domain)
logging.info(f'success! sending generated image')
await message.delete()
if file_id: # img2img
embed.set_image(url=ipfs_link)
orig_url = f'https://{self.ipfs_domain}/ipfs/' + binary_data
res = requests.get(orig_url, stream=True)
if res.status_code == 200:
with io.BytesIO(res.content) as img:
file = discord.File(img, filename='image.png')
embed.set_thumbnail(url='attachment://image.png')
await send(embed=embed, view=SkynetView(self), file=file)
# orig_url = f'https://{self.ipfs_domain}/ipfs/' \
# + binary_data + '/image.png'
# embed.set_thumbnail(
# url=orig_url)
else:
await send(embed=embed, view=SkynetView(self))
else: # txt2img
embed.set_image(url=ipfs_link)
await send(embed=embed, view=SkynetView(self))
return True

View File

@ -1,89 +0,0 @@
# import os
import discord
import asyncio
# from dotenv import load_dotenv
# from pathlib import Path
from discord.ext import commands
from .ui import SkynetView
# # Auth
# current_dir = Path(__file__).resolve().parent
# # parent_dir = current_dir.parent
# env_file_path = current_dir / ".env"
# load_dotenv(dotenv_path=env_file_path)
#
# discordToken = os.getenv("DISCORD_TOKEN")
# Actual Discord bot.
class DiscordBot(commands.Bot):
def __init__(self, bot, *args, **kwargs):
self.bot = bot
intents = discord.Intents(
messages=True,
guilds=True,
typing=True,
members=True,
presences=True,
reactions=True,
message_content=True,
voice_states=True
)
super().__init__(command_prefix='/', intents=intents, *args, **kwargs)
# async def setup_hook(self):
# db.poll_db.start()
async def on_ready(self):
print(f'{self.user.name} has connected to Discord!')
for guild in self.guilds:
for channel in guild.channels:
if channel.name == "skynet":
await channel.send('Skynet bot online', view=SkynetView(self.bot))
# intro_msg = await channel.send('Welcome to the Skynet discord bot.\nSkynet is a decentralized compute layer, focused on supporting AI paradigms. Skynet leverages blockchain technology to manage work requests and fills. We are currently featuring image generation and support 11 different models. Get started with the /help command, or just click on some buttons. Here is an example command to generate an image:\n/txt2img a big red tractor in a giant field of corn')
intro_msg = await channel.send("Welcome to Skynet's Discord Bot,\n\nSkynet operates as a decentralized compute layer, offering a wide array of support for diverse AI paradigms through the use of blockchain technology. Our present focus is image generation, powered by 11 distinct models.\n\nTo begin exploring, use the '/help' command or directly interact with the provided buttons. Here is an example command to generate an image:\n\n'/txt2img a big red tractor in a giant field of corn'")
# await intro_msg.pin()
print("\n==============")
print("Logged in as")
print(self.user.name)
print(self.user.id)
print("==============")
async def on_message(self, message):
if isinstance(message.channel, discord.DMChannel):
return
elif message.channel.name != 'skynet':
return
elif message.author == self.user:
return
await self.process_commands(message)
# await asyncio.sleep(3)
# await message.channel.send('', view=SkynetView(self.bot))
async def on_command_error(self, ctx, error):
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send('You missed a required argument, please try again.')
# async def on_message(self, message):
# print(f"message from {message.author} what he said {message.content}")
# await message.channel.send(message.content)
# bot=DiscordBot()
# @bot.command(name='config', help='Responds with the configuration')
# async def config(ctx):
# response = "This is the bot configuration" # Put your bot configuration here
# await ctx.send(response)
#
# @bot.command(name='helper', help='Responds with a help')
# async def helper(ctx):
# response = "This is help information" # Put your help response here
# await ctx.send(response)
#
# @bot.command(name='txt2img', help='Responds with an image')
# async def txt2img(ctx, *, arg):
# response = f"This is your prompt: {arg}"
# await ctx.send(response)
# bot.run(discordToken)

View File

@ -1,601 +0,0 @@
import io
import json
import logging
from datetime import datetime, timedelta
from PIL import Image
# from telebot.types import CallbackQuery, Message
from skynet.frontend import validate_user_config_request
from skynet.constants import *
from .ui import SkynetView
def create_handler_context(frontend: 'SkynetDiscordFrontend'):
bot = frontend.bot
cleos = frontend.cleos
db_call = frontend.db_call
work_request = frontend.work_request
ipfs_node = frontend.ipfs_node
@bot.command(name='config', help='Responds with the configuration')
async def set_config(ctx):
user = ctx.author
try:
attr, val, reply_txt = validate_user_config_request(
ctx.message.content)
logging.info(f'user config update: {attr} to {val}')
await db_call('update_user_config', user.id, attr, val)
logging.info('done')
except BaseException as e:
reply_txt = str(e)
finally:
await ctx.reply(content=reply_txt, view=SkynetView(frontend))
bot.remove_command('help')
@bot.command(name='help', help='Responds with a help')
async def help(ctx):
splt_msg = ctx.message.content.split(' ')
if len(splt_msg) == 1:
await ctx.send(content=f'```{HELP_TEXT}```', view=SkynetView(frontend))
else:
param = splt_msg[1]
if param in HELP_TOPICS:
await ctx.send(content=f'```{HELP_TOPICS[param]}```', view=SkynetView(frontend))
else:
await ctx.send(content=f'```{HELP_UNKWNOWN_PARAM}```', view=SkynetView(frontend))
@bot.command(name='cool', help='Display a list of cool prompt words')
async def send_cool_words(ctx):
clean_cool_word = '\n'.join(CLEAN_COOL_WORDS)
await ctx.send(content=f'```{clean_cool_word}```', view=SkynetView(frontend))
@bot.command(name='stats', help='See user statistics')
async def user_stats(ctx):
user = ctx.author
await db_call('get_or_create_user', user.id)
generated, joined, role = await db_call('get_user_stats', user.id)
stats_str = f'```generated: {generated}\n'
stats_str += f'joined: {joined}\n'
stats_str += f'role: {role}\n```'
await ctx.reply(stats_str, view=SkynetView(frontend))
@bot.command(name='donate', help='See donate info')
async def donation_info(ctx):
await ctx.reply(
f'```\n{DONATION_INFO}```', view=SkynetView(frontend))
@bot.command(name='txt2img', help='Responds with an image')
async def send_txt2img(ctx):
# grab user from ctx
user = ctx.author
user_row = await db_call('get_or_create_user', user.id)
# init new msg
init_msg = 'started processing txt2img request...'
status_msg = await ctx.send(init_msg)
await db_call(
'new_user_request', user.id, ctx.message.id, status_msg.id, status=init_msg)
prompt = ' '.join(ctx.message.content.split(' ')[1:])
if len(prompt) == 0:
await status_msg.edit(content='Empty text prompt ignored.'
)
await db_call('update_user_request', status_msg.id, 'Empty text prompt ignored.')
return
logging.info(f'mid: {ctx.message.id}')
user_config = {**user_row}
del user_config['id']
params = {
'prompt': prompt,
**user_config
}
await db_call(
'update_user_stats', user.id, 'txt2img', last_prompt=prompt)
success = await work_request(user, status_msg, 'txt2img', params, ctx)
if success:
await db_call('increment_generated', user.id)
@bot.command(name='redo', help='Redo last request')
async def redo(ctx):
init_msg = 'started processing redo request...'
status_msg = await ctx.send(init_msg)
user = ctx.author
method = await db_call('get_last_method_of', user.id)
prompt = await db_call('get_last_prompt_of', user.id)
file_id = None
binary = ''
if method == 'img2img':
file_id = await db_call('get_last_file_of', user.id)
binary = await db_call('get_last_binary_of', user.id)
if not prompt:
await status_msg.edit(
content='no last prompt found, do a txt2img cmd first!',
view=SkynetView(frontend)
)
return
user_row = await db_call('get_or_create_user', user.id)
await db_call(
'new_user_request', user.id, ctx.message.id, status_msg.id, status=init_msg)
user_config = {**user_row}
del user_config['id']
params = {
'prompt': prompt,
**user_config
}
success = await work_request(
user, status_msg, 'redo', params, ctx,
file_id=file_id,
binary_data=binary
)
if success:
await db_call('increment_generated', user.id)
@bot.command(name='img2img', help='Responds with an image')
async def send_img2img(ctx):
# if isinstance(message_or_query, CallbackQuery):
# query = message_or_query
# message = query.message
# user = query.from_user
# chat = query.message.chat
#
# else:
# message = message_or_query
# user = message.from_user
# chat = message.chat
# reply_id = None
# if chat.type == 'group' and chat.id == GROUP_ID:
# reply_id = message.message_id
#
user = ctx.author
user_row = await db_call('get_or_create_user', user.id)
# init new msg
init_msg = 'started processing img2img request...'
status_msg = await ctx.send(init_msg)
await db_call(
'new_user_request', user.id, ctx.message.id, status_msg.id, status=init_msg)
if not ctx.message.content.startswith('/img2img'):
await ctx.reply(
'For image to image you need to add /img2img to the beggining of your caption'
)
return
prompt = ' '.join(ctx.message.content.split(' ')[1:])
if len(prompt) == 0:
await ctx.reply('Empty text prompt ignored.')
return
# file_id = message.photo[-1].file_id
# file_path = (await bot.get_file(file_id)).file_path
# image_raw = await bot.download_file(file_path)
#
file = ctx.message.attachments[-1]
file_id = str(file.id)
# file bytes
image_raw = await file.read()
user_config = {**user_row}
del user_config['id']
with Image.open(io.BytesIO(image_raw)) as image:
w, h = image.size
if w > user_config['width'] or h > user_config['height']:
logging.warning(f'user sent img of size {image.size}')
image.thumbnail(
(user_config['width'], user_config['height']))
logging.warning(f'resized it to {image.size}')
# if w > 512 or h > 512:
# logging.warning(f'user sent img of size {image.size}')
# image.thumbnail((512, 512))
# logging.warning(f'resized it to {image.size}')
# image.save(f'ipfs-docker-staging/image.png', format='PNG')
image_loc = 'ipfs-staging/image.png'
image.save(image_loc, format='PNG')
ipfs_info = await ipfs_node.add(image_loc)
ipfs_hash = ipfs_info['Hash']
await ipfs_node.pin(ipfs_hash)
logging.info(f'published input image {ipfs_hash} on ipfs')
logging.info(f'mid: {ctx.message.id}')
params = {
'prompt': prompt,
**user_config
}
await db_call(
'update_user_stats',
user.id,
'img2img',
last_prompt=prompt,
last_file=file_id,
last_binary=ipfs_hash
)
success = await work_request(
user, status_msg, 'img2img', params, ctx,
file_id=file_id,
binary_data=ipfs_hash
)
if success:
await db_call('increment_generated', user.id)
# TODO: DELETE BELOW
# user = 'testworker3'
# status_msg = 'status'
# params = {
# 'prompt': arg,
# 'seed': None,
# 'step': 35,
# 'guidance': 7.5,
# 'strength': 0.5,
# 'width': 512,
# 'height': 512,
# 'upscaler': None,
# 'model': 'prompthero/openjourney',
# }
#
# ec = await work_request(user, status_msg, 'txt2img', params, ctx)
# print(ec)
# if ec == 0:
# await db_call('increment_generated', user.id)
# response = f"This is your prompt: {arg}"
# await ctx.send(response)
# generic / simple handlers
# @bot.message_handler(commands=['help'])
# async def send_help(message):
# splt_msg = message.text.split(' ')
#
# if len(splt_msg) == 1:
# await bot.reply_to(message, HELP_TEXT)
#
# else:
# param = splt_msg[1]
# if param in HELP_TOPICS:
# await bot.reply_to(message, HELP_TOPICS[param])
#
# else:
# await bot.reply_to(message, HELP_UNKWNOWN_PARAM)
#
# @bot.message_handler(commands=['cool'])
# async def send_cool_words(message):
# await bot.reply_to(message, '\n'.join(COOL_WORDS))
#
# @bot.message_handler(commands=['queue'])
# async def queue(message):
# an_hour_ago = datetime.now() - timedelta(hours=1)
# queue = await cleos.aget_table(
# 'gpu.scd', 'gpu.scd', 'queue',
# index_position=2,
# key_type='i64',
# sort='desc',
# lower_bound=int(an_hour_ago.timestamp())
# )
# await bot.reply_to(
# message, f'Total requests on skynet queue: {len(queue)}')
# @bot.message_handler(commands=['config'])
# async def set_config(message):
# user = message.from_user.id
# try:
# attr, val, reply_txt = validate_user_config_request(
# message.text)
#
# logging.info(f'user config update: {attr} to {val}')
# await db_call('update_user_config', user, attr, val)
# logging.info('done')
#
# except BaseException as e:
# reply_txt = str(e)
#
# finally:
# await bot.reply_to(message, reply_txt)
#
# @bot.message_handler(commands=['stats'])
# async def user_stats(message):
# user = message.from_user.id
#
# await db_call('get_or_create_user', user)
# generated, joined, role = await db_call('get_user_stats', user)
#
# stats_str = f'generated: {generated}\n'
# stats_str += f'joined: {joined}\n'
# stats_str += f'role: {role}\n'
#
# await bot.reply_to(
# message, stats_str)
#
# @bot.message_handler(commands=['donate'])
# async def donation_info(message):
# await bot.reply_to(
# message, DONATION_INFO)
#
# @bot.message_handler(commands=['say'])
# async def say(message):
# chat = message.chat
# user = message.from_user
#
# if (chat.type == 'group') or (user.id != 383385940):
# return
#
# await bot.send_message(GROUP_ID, message.text[4:])
# generic txt2img handler
# async def _generic_txt2img(message_or_query):
# if isinstance(message_or_query, CallbackQuery):
# query = message_or_query
# message = query.message
# user = query.from_user
# chat = query.message.chat
#
# else:
# message = message_or_query
# user = message.from_user
# chat = message.chat
#
# reply_id = None
# if chat.type == 'group' and chat.id == GROUP_ID:
# reply_id = message.message_id
#
# user_row = await db_call('get_or_create_user', user.id)
#
# # init new msg
# init_msg = 'started processing txt2img request...'
# status_msg = await bot.reply_to(message, init_msg)
# await db_call(
# 'new_user_request', user.id, message.id, status_msg.id, status=init_msg)
#
# prompt = ' '.join(message.text.split(' ')[1:])
#
# if len(prompt) == 0:
# await bot.edit_message_text(
# 'Empty text prompt ignored.',
# chat_id=status_msg.chat.id,
# message_id=status_msg.id
# )
# await db_call('update_user_request', status_msg.id, 'Empty text prompt ignored.')
# return
#
# logging.info(f'mid: {message.id}')
#
# user_config = {**user_row}
# del user_config['id']
#
# params = {
# 'prompt': prompt,
# **user_config
# }
#
# await db_call(
# 'update_user_stats', user.id, 'txt2img', last_prompt=prompt)
#
# ec = await work_request(user, status_msg, 'txt2img', params)
# if ec == 0:
# await db_call('increment_generated', user.id)
#
#
# # generic img2img handler
#
# async def _generic_img2img(message_or_query):
# if isinstance(message_or_query, CallbackQuery):
# query = message_or_query
# message = query.message
# user = query.from_user
# chat = query.message.chat
#
# else:
# message = message_or_query
# user = message.from_user
# chat = message.chat
#
# reply_id = None
# if chat.type == 'group' and chat.id == GROUP_ID:
# reply_id = message.message_id
#
# user_row = await db_call('get_or_create_user', user.id)
#
# # init new msg
# init_msg = 'started processing txt2img request...'
# status_msg = await bot.reply_to(message, init_msg)
# await db_call(
# 'new_user_request', user.id, message.id, status_msg.id, status=init_msg)
#
# if not message.caption.startswith('/img2img'):
# await bot.reply_to(
# message,
# 'For image to image you need to add /img2img to the beggining of your caption'
# )
# return
#
# prompt = ' '.join(message.caption.split(' ')[1:])
#
# if len(prompt) == 0:
# await bot.reply_to(message, 'Empty text prompt ignored.')
# return
#
# file_id = message.photo[-1].file_id
# file_path = (await bot.get_file(file_id)).file_path
# image_raw = await bot.download_file(file_path)
# with Image.open(io.BytesIO(image_raw)) as image:
# w, h = image.size
#
# if w > 512 or h > 512:
# logging.warning(f'user sent img of size {image.size}')
# image.thumbnail((512, 512))
# logging.warning(f'resized it to {image.size}')
#
# image.save(f'ipfs-docker-staging/image.png', format='PNG')
#
# ipfs_hash = ipfs_node.add('image.png')
# ipfs_node.pin(ipfs_hash)
#
# logging.info(f'published input image {ipfs_hash} on ipfs')
#
# logging.info(f'mid: {message.id}')
#
# user_config = {**user_row}
# del user_config['id']
#
# params = {
# 'prompt': prompt,
# **user_config
# }
#
# await db_call(
# 'update_user_stats',
# user.id,
# 'img2img',
# last_file=file_id,
# last_prompt=prompt,
# last_binary=ipfs_hash
# )
#
# ec = await work_request(
# user, status_msg, 'img2img', params,
# file_id=file_id,
# binary_data=ipfs_hash
# )
#
# if ec == 0:
# await db_call('increment_generated', user.id)
#
# generic redo handler
# async def _redo(message_or_query):
# is_query = False
# if isinstance(message_or_query, CallbackQuery):
# is_query = True
# query = message_or_query
# message = query.message
# user = query.from_user
# chat = query.message.chat
#
# elif isinstance(message_or_query, Message):
# message = message_or_query
# user = message.from_user
# chat = message.chat
#
# init_msg = 'started processing redo request...'
# if is_query:
# status_msg = await bot.send_message(chat.id, init_msg)
#
# else:
# status_msg = await bot.reply_to(message, init_msg)
#
# method = await db_call('get_last_method_of', user.id)
# prompt = await db_call('get_last_prompt_of', user.id)
#
# file_id = None
# binary = ''
# if method == 'img2img':
# file_id = await db_call('get_last_file_of', user.id)
# binary = await db_call('get_last_binary_of', user.id)
#
# if not prompt:
# await bot.reply_to(
# message,
# 'no last prompt found, do a txt2img cmd first!'
# )
# return
#
#
# user_row = await db_call('get_or_create_user', user.id)
# await db_call(
# 'new_user_request', user.id, message.id, status_msg.id, status=init_msg)
# user_config = {**user_row}
# del user_config['id']
#
# params = {
# 'prompt': prompt,
# **user_config
# }
#
# await work_request(
# user, status_msg, 'redo', params,
# file_id=file_id,
# binary_data=binary
# )
# "proxy" handlers just request routers
# @bot.message_handler(commands=['txt2img'])
# async def send_txt2img(message):
# await _generic_txt2img(message)
#
# @bot.message_handler(func=lambda message: True, content_types=[
# 'photo', 'document'])
# async def send_img2img(message):
# await _generic_img2img(message)
#
# @bot.message_handler(commands=['img2img'])
# async def img2img_missing_image(message):
# await bot.reply_to(
# message,
# 'seems you tried to do an img2img command without sending image'
# )
#
# @bot.message_handler(commands=['redo'])
# async def redo(message):
# await _redo(message)
#
# @bot.callback_query_handler(func=lambda call: True)
# async def callback_query(call):
# msg = json.loads(call.data)
# logging.info(call.data)
# method = msg.get('method')
# match method:
# case 'redo':
# await _redo(call)
# catch all handler for things we dont support
# @bot.message_handler(func=lambda message: True)
# async def echo_message(message):
# if message.text[0] == '/':
# await bot.reply_to(message, UNKNOWN_CMD_TEXT)

View File

@ -1,325 +0,0 @@
import io
import discord
from PIL import Image
import logging
from skynet.constants import *
from skynet.frontend import validate_user_config_request
class SkynetView(discord.ui.View):
def __init__(self, bot):
self.bot = bot
super().__init__(timeout=None)
self.add_item(RedoButton(
'redo', discord.ButtonStyle.primary, self.bot))
self.add_item(Txt2ImgButton(
'txt2img', discord.ButtonStyle.primary, self.bot))
self.add_item(Img2ImgButton(
'img2img', discord.ButtonStyle.primary, self.bot))
self.add_item(StatsButton(
'stats', discord.ButtonStyle.secondary, self.bot))
self.add_item(DonateButton(
'donate', discord.ButtonStyle.secondary, self.bot))
self.add_item(ConfigButton(
'config', discord.ButtonStyle.secondary, self.bot))
self.add_item(HelpButton(
'help', discord.ButtonStyle.secondary, self.bot))
self.add_item(CoolButton(
'cool', discord.ButtonStyle.secondary, self.bot))
class Txt2ImgButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
db_call = self.bot.db_call
work_request = self.bot.work_request
msg = await grab('Enter your prompt:', interaction)
# grab user from msg
user = msg.author
user_row = await db_call('get_or_create_user', user.id)
# init new msg
init_msg = 'started processing txt2img request...'
status_msg = await msg.channel.send(init_msg)
await db_call(
'new_user_request', user.id, msg.id, status_msg.id, status=init_msg)
prompt = msg.content
if len(prompt) == 0:
await status_msg.edit(content='Empty text prompt ignored.'
)
await db_call('update_user_request', status_msg.id, 'Empty text prompt ignored.')
return
logging.info(f'mid: {msg.id}')
user_config = {**user_row}
del user_config['id']
params = {
'prompt': prompt,
**user_config
}
await db_call(
'update_user_stats', user.id, 'txt2img', last_prompt=prompt)
success = await work_request(user, status_msg, 'txt2img', params, msg)
if success:
await db_call('increment_generated', user.id)
class Img2ImgButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
db_call = self.bot.db_call
work_request = self.bot.work_request
ipfs_node = self.bot.ipfs_node
msg = await grab('Attach an Image. Enter your prompt:', interaction)
user = msg.author
user_row = await db_call('get_or_create_user', user.id)
# init new msg
init_msg = 'started processing img2img request...'
status_msg = await msg.channel.send(init_msg)
await db_call(
'new_user_request', user.id, msg.id, status_msg.id, status=init_msg)
# if not msg.content.startswith('/img2img'):
# await msg.reply(
# 'For image to image you need to add /img2img to the beggining of your caption'
# )
# return
prompt = msg.content
if len(prompt) == 0:
await msg.reply('Empty text prompt ignored.')
return
# file_id = message.photo[-1].file_id
# file_path = (await bot.get_file(file_id)).file_path
# image_raw = await bot.download_file(file_path)
#
file = msg.attachments[-1]
file_id = str(file.id)
# file bytes
image_raw = await file.read()
user_config = {**user_row}
del user_config['id']
with Image.open(io.BytesIO(image_raw)) as image:
w, h = image.size
if w > user_config['width'] or h > user_config['height']:
logging.warning(f'user sent img of size {image.size}')
image.thumbnail(
(user_config['width'], user_config['height']))
logging.warning(f'resized it to {image.size}')
# if w > 512 or h > 512:
# logging.warning(f'user sent img of size {image.size}')
# image.thumbnail((512, 512))
# logging.warning(f'resized it to {image.size}')
# image.save(f'ipfs-docker-staging/image.png', format='PNG')
image_loc = 'ipfs-staging/image.png'
image.save(image_loc, format='PNG')
ipfs_info = await ipfs_node.add(image_loc)
ipfs_hash = ipfs_info['Hash']
await ipfs_node.pin(ipfs_hash)
logging.info(f'published input image {ipfs_hash} on ipfs')
logging.info(f'mid: {msg.id}')
params = {
'prompt': prompt,
**user_config
}
await db_call(
'update_user_stats',
user.id,
'img2img',
last_prompt=prompt,
last_file=file_id,
last_binary=ipfs_hash
)
success = await work_request(
user, status_msg, 'img2img', params, msg,
file_id=file_id,
binary_data=ipfs_hash
)
if success:
await db_call('increment_generated', user.id)
class RedoButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
db_call = self.bot.db_call
work_request = self.bot.work_request
init_msg = 'started processing redo request...'
await interaction.response.send_message(init_msg)
status_msg = await interaction.original_response()
user = interaction.user
method = await db_call('get_last_method_of', user.id)
prompt = await db_call('get_last_prompt_of', user.id)
file_id = None
binary = ''
if method == 'img2img':
file_id = await db_call('get_last_file_of', user.id)
binary = await db_call('get_last_binary_of', user.id)
if not prompt:
await status_msg.edit(
content='no last prompt found, do a txt2img cmd first!',
view=SkynetView(self.bot)
)
return
user_row = await db_call('get_or_create_user', user.id)
await db_call(
'new_user_request', user.id, interaction.id, status_msg.id, status=init_msg)
user_config = {**user_row}
del user_config['id']
params = {
'prompt': prompt,
**user_config
}
success = await work_request(
user, status_msg, 'redo', params, interaction,
file_id=file_id,
binary_data=binary
)
if success:
await db_call('increment_generated', user.id)
class ConfigButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
db_call = self.bot.db_call
msg = await grab('What params do you want to change? (format: <param> <value>)', interaction)
user = interaction.user
try:
attr, val, reply_txt = validate_user_config_request(
'/config ' + msg.content)
logging.info(f'user config update: {attr} to {val}')
await db_call('update_user_config', user.id, attr, val)
logging.info('done')
except BaseException as e:
reply_txt = str(e)
finally:
await msg.reply(content=reply_txt, view=SkynetView(self.bot))
class StatsButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
db_call = self.bot.db_call
user = interaction.user
await db_call('get_or_create_user', user.id)
generated, joined, role = await db_call('get_user_stats', user.id)
stats_str = f'```generated: {generated}\n'
stats_str += f'joined: {joined}\n'
stats_str += f'role: {role}\n```'
await interaction.response.send_message(
content=stats_str, view=SkynetView(self.bot))
class DonateButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
await interaction.response.send_message(
content=f'```\n{DONATION_INFO}```',
view=SkynetView(self.bot))
class CoolButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
clean_cool_word = '\n'.join(CLEAN_COOL_WORDS)
await interaction.response.send_message(
content=f'```{clean_cool_word}```',
view=SkynetView(self.bot))
class HelpButton(discord.ui.Button):
def __init__(self, label: str, style: discord.ButtonStyle, bot):
self.bot = bot
super().__init__(label=label, style=style)
async def callback(self, interaction):
msg = await grab('What would you like help with? (a for all)', interaction)
param = msg.content
if param == 'a':
await msg.reply(content=f'```{HELP_TEXT}```', view=SkynetView(self.bot))
else:
if param in HELP_TOPICS:
await msg.reply(content=f'```{HELP_TOPICS[param]}```', view=SkynetView(self.bot))
else:
await msg.reply(content=f'```{HELP_UNKWNOWN_PARAM}```', view=SkynetView(self.bot))
async def grab(prompt, interaction):
def vet(m):
return m.author == interaction.user and m.channel == interaction.channel
await interaction.response.send_message(prompt, ephemeral=True)
message = await interaction.client.wait_for('message', check=vet)
return message

View File

@ -1,123 +0,0 @@
import json
import logging
import traceback
from datetime import datetime, timezone
from telebot.types import InlineKeyboardButton, InlineKeyboardMarkup
from telebot.async_telebot import ExceptionHandler
from telebot.formatting import hlink
import discord
from skynet.constants import *
def timestamp_pretty():
return datetime.now(timezone.utc).strftime('%H:%M:%S')
def tg_user_pretty(tguser):
if tguser.username:
return f'@{tguser.username}'
else:
return f'{tguser.first_name} id: {tguser.id}'
class SKYExceptionHandler(ExceptionHandler):
def handle(exception):
traceback.print_exc()
def build_redo_menu():
btn_redo = InlineKeyboardButton(
"Redo", callback_data=json.dumps({'method': 'redo'}))
inline_keyboard = InlineKeyboardMarkup()
inline_keyboard.add(btn_redo)
return inline_keyboard
def prepare_metainfo_caption(user, worker: str, reward: str, meta: dict, embed) -> str:
prompt = meta["prompt"]
if len(prompt) > 256:
prompt = prompt[:256]
gen_str = f'generated by {user.name}\n'
gen_str += f'performed by {worker}\n'
gen_str += f'reward: {reward}\n'
embed.add_field(
name='General Info', value=f'```{gen_str}```', inline=False)
# meta_str = f'__by {user.name}__\n'
# meta_str += f'*performed by {worker}*\n'
# meta_str += f'__**reward: {reward}**__\n'
embed.add_field(name='Prompt', value=f'```{prompt}\n```', inline=False)
# meta_str = f'`prompt:` {prompt}\n'
meta_str = f'seed: {meta["seed"]}\n'
meta_str += f'step: {meta["step"]}\n'
meta_str += f'guidance: {meta["guidance"]}\n'
if meta['strength']:
meta_str += f'strength: {meta["strength"]}\n'
meta_str += f'algo: {meta["model"]}\n'
if meta['upscaler']:
meta_str += f'upscaler: {meta["upscaler"]}\n'
embed.add_field(name='Parameters', value=f'```{meta_str}```', inline=False)
foot_str = f'Made with Skynet v{VERSION}\n'
foot_str += f'JOIN THE SWARM: https://discord.gg/PAabjJtZAF'
embed.set_footer(text=foot_str)
return meta_str
def generate_reply_caption(
user, # discord user
params: dict,
tx_hash: str,
worker: str,
reward: str,
explorer_domain: str
):
explorer_link = discord.Embed(
title='[SKYNET Transaction Explorer]',
url=f'https://{explorer_domain}/v2/explore/transaction/{tx_hash}',
color=discord.Color.blue())
meta_info = prepare_metainfo_caption(
user, worker, reward, params, explorer_link)
# why do we have this?
final_msg = '\n'.join([
'Worker finished your task!',
# explorer_link,
f'PARAMETER INFO:\n{meta_info}'
])
# final_msg += '\n'.join([
# # f'***{explorer_link}***',
# f'{meta_info}'
# ])
logging.info(final_msg)
return final_msg, explorer_link
async def get_global_config(cleos):
return (await cleos.aget_table(
'gpu.scd', 'gpu.scd', 'config'))[0]
async def get_user_nonce(cleos, user: str):
return (await cleos.aget_table(
'gpu.scd', 'gpu.scd', 'users',
index_position=1,
key_type='name',
lower_bound=user,
upper_bound=user
))[0]['nonce']