fresh start

This commit is contained in:
2026-01-08 22:07:03 +01:00
commit 715be97289
30 changed files with 2302 additions and 0 deletions

236
Discord/discordHelper.py Normal file
View File

@@ -0,0 +1,236 @@
from __future__ import annotations
from datetime import datetime
import html
import io
import re
import nextcord
from nextcord import ChannelType, Message
from Database.dbcontroller import DatabaseController
from Database.x_classes import ErrorID, x_accounts
from Twitter.tweetHelper import DownloadedMedia
from exceptions import NO_CHANNEL, OTHER_ERROR
from tweety.types.twDataTypes import Tweet
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from runtimeBotData import RuntimeBotData
def chunks(s, n):
"""Produce `n`-character chunks from `s`."""
for start in range(0, len(s), n):
yield s[start:start+n]
def build_pixiv_embed(post):
url = "https://www.pixiv.net/en/artworks/" + str(post.id)
text = re.sub(r"https://t.co\S+", "", html.unescape(post.caption))
date = post.create_date
embed=nextcord.Embed(description=text)
embed.set_author(name=post.user.name, url=url)
embed.set_footer(text=date)
return embed
def build_x_embed(handle : str, post : Tweet):
url = "https://x.com/" + handle + "/status/" + str(post.id)
text = re.sub(r"https://t.co\S+", "", html.unescape(post.text))
date = datetime.strftime(post.created_on, '%Y-%m-%d %H:%M:%S')
embed=nextcord.Embed(description=text)
embed.set_author(name=handle, url=url, icon_url=post.author.profile_image_url_https)
embed.set_footer(text=date)
return embed
def build_secondary_embed(main_post : Message, handle : str, post : Tweet):
text = re.sub(r"https://t.co\S+", "", html.unescape(post.text))
date = datetime.strftime(post.created_on, '%Y-%m-%d %H:%M:%S')
attachment_urls = [attachment.url.split("?")[0] for attachment in main_post.attachments]
embeds = []
for url in attachment_urls:
embed = nextcord.Embed(url="https://katworks.sytes.net")
embed.set_image(url)
embeds.append(embed)
if len(embeds) == 0:
return None
embed : nextcord.Embed = embeds[0]
embed.description = text
embed.set_footer(text=date)
embed.set_author(name=handle, url=main_post.jump_url, icon_url=post.author.profile_image_url_https)
return embeds
async def send_error(ex : Exception, botData : RuntimeBotData):
print(ex)
errors_channel = nextcord.utils.get(botData.client.guilds[0].channels, name="bot-status")
await errors_channel.send(content=str(ex))
def get_secondary_channel(is_animated, is_filtered, rating, tags : list, artist : x_accounts, guild : nextcord.Guild):
if is_animated:
return nextcord.utils.get(guild.channels, name="animation-feed")
if artist.rating == 'NSFL':
return nextcord.utils.get(guild.channels, name="hidden-feed")
if "futanari" in tags:
return nextcord.utils.get(guild.channels, name="hidden-feed")
if is_filtered or not rating:
return nextcord.utils.get(guild.channels, name="filtered-feed")
if rating == "general":
return nextcord.utils.get(guild.channels, name="safe-feed")
if rating == "sensitive" or rating == "questionable":
return nextcord.utils.get(guild.channels, name="unsafe-feed")
if rating == 'explicit':
return nextcord.utils.get(guild.channels, name="explicit-feed")
return nextcord.utils.get(guild.channels, name="filtered-feed")
async def edit_existing_embed_color(message : Message, color : nextcord.Colour):
embeds = message.embeds
embeds[0].colour = color
await message.edit(embeds=embeds)
async def send_x_post(post : Tweet, artist : x_accounts, guild : nextcord.Guild, new_accounts : list, files_to_send : list[DownloadedMedia], is_filtered: bool, rating: str, tags : list, auto_approve : bool = False, vox_labels : list = None, duplicate_posts : list = None, xView: nextcord.ui.View = None, yView: nextcord.ui.View = None):
if vox_labels is None:
vox_labels = []
if duplicate_posts is None:
duplicate_posts = []
if artist.discord_channel_id != 0:
channel = guild.get_channel(artist.discord_channel_id)
elif artist.discord_thread_id != 0:
channel = guild.get_thread(artist.discord_thread_id)
else:
raise NO_CHANNEL("Ensure channel for the account exists")
embed = build_x_embed(artist.name, post)
if rating != "":
embed.add_field(name = "rating", value=rating, inline=False)
embed.add_field(name = "tags", value=f'{", ".join(tags)}'.replace("_","\\_")[:1024], inline=False)
if len(duplicate_posts) > 0:
links = [f"[{id}](<https://fxtwitter.com/i/status/{id}>)" for id in duplicate_posts]
embed.add_field(name = "duplicates", value=f'{" ".join(links)}'[:1024], inline=False)
discord_files = [nextcord.File(fp = io.BytesIO(x.file_bytes), filename = x.file_name, force_close=True) for x in files_to_send]
try:
main_post : Message = await channel.send(embed=embed, files=discord_files)
except Exception as e:
raise OTHER_ERROR(e)
finally:
for file in discord_files: file.close()
#skip posting in the public feed for accounts with too many posts
if artist.name in new_accounts and not is_filtered:
return main_post
is_animated = files_to_send[0].file_name.endswith(".mp4")
secondary_channel : nextcord.TextChannel = get_secondary_channel(is_animated, is_filtered, rating, tags, artist, guild)
if is_animated:
link = "https://vxtwitter.com/" + artist.name + "/status/" + post.id + " " + main_post.jump_url
secondary_post : Message = await secondary_channel.send(content=link, view = None if auto_approve else yView)
else:
embeds = build_secondary_embed(main_post, artist.name, post)
if rating != "":
embeds[0].add_field(name = "rating", value=rating, inline=False)
#embeds[0].add_field(name = "tags", value=f'{", ".join(tags)}'.replace("_","\\_")[:1024], inline=False)
if len(vox_labels) > 0:
embeds[0].add_field(name = "prediction", value=", ".join(vox_labels), inline=False)
if len(duplicate_posts) > 0:
links = [f"[{id}](<https://fxtwitter.com/i/status/{id}>)" for id in duplicate_posts]
embeds[0].add_field(name = "duplicates", value=f'{" ".join(links)}'[:1024], inline=False)
secondary_post : Message = await secondary_channel.send(embeds=embeds, view = None if auto_approve else xView)
return main_post
async def send_pixiv_post(post, files_to_send, channel : nextcord.TextChannel):
embed = build_pixiv_embed(post)
discord_files = [nextcord.File(fp = io.BytesIO(file["file"]), filename = file["name"], force_close=True) for file in files_to_send]
try:
main_post : Message = await channel.send(embed=embed, files=discord_files)
except Exception as e:
print(e)
return None, ErrorID.OTHER_ERROR
finally:
for file in discord_files: file.close()
return main_post, ErrorID.SUCCESS
async def post_result(results, guild : nextcord.Guild, new_accounts : list):
print("Results: ", len(results))
channel = nextcord.utils.get(guild.channels, name="bot-status")
string = f'Last check: {datetime.now().strftime("%d/%m/%Y %H:%M:%S")}'
for result in results:
if result in new_accounts:
string += f'\n{result} (new): {results[result]}'
new_accounts.remove(result)
else:
string += f'\n{result}: {results[result]}'
for chunk in chunks(string, 6000):
embed = nextcord.Embed(description=chunk)
await channel.send(embed=embed)
async def get_channel_from_handle(guild: nextcord.Guild, handle : str):
channel = nextcord.utils.get(guild.channels, name=handle.lower())
if channel is None:
catId = 0
category = None
while category is None or len(category.channels) == 50:
category = nextcord.utils.get(guild.categories, name=f'TWITTER-{catId}')
if category is None:
category = await guild.create_category(f'TWITTER-{catId}')
catId+=1
channel = await guild.create_text_channel(name=handle.lower(), category=category)
return channel
async def get_thread_from_handle(guild: nextcord.Guild, handle : str):
channel = nextcord.utils.get(guild.channels, name="threads")
thread = nextcord.utils.get(channel.threads, name=handle.lower())
if thread is None:
thread = await channel.create_thread(name=handle.lower(), auto_archive_duration=10080, type=ChannelType.public_thread)
return thread
async def get_category_by_name(client: nextcord.Client, name):
guild = client.guilds[0]
category = nextcord.utils.get(guild.categories, name=name)
if category is None:
category = await guild.create_category(name)
return category
def check_permission(interaction : nextcord.Interaction):
role = nextcord.utils.find(lambda r: r.name == 'Archivist', interaction.guild.roles)
return role in interaction.user.roles
async def message_from_jump_url(server : nextcord.Guild, jump_url:str):
link = jump_url.split('/')
server_id = int(link[4])
channel_id = int(link[5])
msg_id = int(link[6])
channel = server.get_channel(channel_id)
if channel is None:
channel = server.get_thread(channel_id)
message = await channel.fetch_message(msg_id)
return message
async def get_main_post_and_data(guild, jump_url, botData : RuntimeBotData):
main_post = await message_from_jump_url(guild, jump_url)
tw_embed = main_post.embeds[0]
x_post_id = int(tw_embed.author.url.split('/')[-1])
return main_post, x_post_id
async def ensure_has_channel_or_thread(artist: x_accounts, guild: nextcord.Guild, database: DatabaseController):
if artist.discord_channel_id == 0 and artist.discord_thread_id == 0:
try:
channel = await get_channel_from_handle(guild, artist.name)
artist.discord_channel_id = channel.id
artist.discord_thread_id = 0
except:
thread = await get_thread_from_handle(guild, artist.name)
artist.discord_thread_id = thread.id
artist.discord_channel_id = 0
database.x_update_account(artist)