2023-08-11 15:55:44 +10:00
import os
from dotenv import load_dotenv
import discord
from discord import app_commands
2023-08-11 16:35:36 +10:00
import requests
2023-08-11 17:24:11 +10:00
import dns . resolver
2023-08-12 22:39:36 +10:00
import markdownify
2023-09-27 17:27:06 +10:00
import subprocess
import tempfile
2023-09-27 17:35:01 +10:00
import re
2023-09-27 17:44:07 +10:00
import binascii
2023-09-27 18:29:09 +10:00
from cryptography import x509
from cryptography . hazmat . backends import default_backend
2023-10-01 13:21:13 +11:00
import datetime
2023-11-13 20:16:01 +11:00
import chatai
2023-11-14 14:01:29 +11:00
import tools
from tools import parse_time , read_reminders , store_reminder , write_reminders
2023-11-14 13:05:41 +11:00
import asyncio
2023-11-14 13:56:55 +11:00
from discord . ext import tasks , commands
2023-10-01 22:38:31 +11:00
2023-09-27 18:25:58 +10:00
2023-08-11 15:55:44 +10:00
load_dotenv ( )
TOKEN = os . getenv ( ' DISCORD_TOKEN ' )
2023-08-12 17:14:55 +10:00
ADMINID = 0
2023-08-12 16:52:01 +10:00
KUTT_APIKEY = os . getenv ( ' LINK_API_KEY ' )
KUTT_URL = os . getenv ( ' LINK_URL ' )
2023-08-12 17:05:35 +10:00
LOG_CHANNEL = int ( os . getenv ( ' LOG_CHANNEL ' ) )
2023-08-11 15:55:44 +10:00
intents = discord . Intents . default ( )
client = discord . Client ( intents = intents )
tree = app_commands . CommandTree ( client )
2023-08-11 16:35:36 +10:00
activityMessage = " over the server "
statusType = " watching "
2023-08-12 16:52:01 +10:00
2023-08-11 15:55:44 +10:00
# Commands
@tree.command ( name = " ping " , description = " Check bot connection " )
async def ping ( ctx ) :
await ctx . response . send_message ( " Pong! " , ephemeral = True )
2023-08-11 16:35:36 +10:00
@tree.command ( name = " shortlink " , description = " Shorten a link " )
async def shortlink ( ctx , link : str , name : str = None ) :
2023-08-12 16:52:01 +10:00
if ( ctx . user . id != ADMINID ) :
2023-08-11 16:55:18 +10:00
await log ( " User: " + str ( ctx . user . name ) + " tried to use the shortlink command " )
2023-08-11 16:35:36 +10:00
await ctx . response . send_message ( " You don ' t have permission to use this command " , ephemeral = True )
else :
2023-08-12 17:05:35 +10:00
url = f " { KUTT_URL } /api/v2/links "
2023-08-12 16:52:01 +10:00
headers = { ' X-API-KEY ' : KUTT_APIKEY }
2023-08-11 16:35:36 +10:00
data = { ' target ' : link , ' customurl ' : name }
if ( name == None ) :
data = { ' target ' : link }
x = requests . post ( url , data = data , headers = headers )
if ( x . status_code != 200 and x . status_code != 201 ) :
await ctx . response . send_message ( " ERROR: " + x . text , ephemeral = True )
link = x . json ( ) [ ' link ' ]
await ctx . response . send_message ( " Link: " + link , ephemeral = False )
@tree.command ( name = " botstatus " , description = " Set the bot status " )
async def botstatus ( ctx , message : str , statusmethod : str = None ) :
2023-08-12 16:52:01 +10:00
if ( ctx . user . id != ADMINID ) :
2023-08-11 16:55:18 +10:00
await log ( " User: " + str ( ctx . user . name ) + " tried to use the botstatus command " )
2023-08-11 16:35:36 +10:00
await ctx . response . send_message ( " You don ' t have permission to use this command " , ephemeral = True )
else :
global activityMessage
activityMessage = message
global statusType
if ( statusmethod == None ) :
statusmethod = " watching "
else :
statusType = statusmethod . lower ( )
updateStatus ( )
await ctx . response . send_message ( " Status updated " , ephemeral = True )
2023-08-11 15:55:44 +10:00
2023-08-11 17:24:11 +10:00
@tree.command ( name = " dig " , description = " Dig a dns record " )
async def dig ( ctx , domain : str , record_type : str = " A " ) :
record_type = record_type . upper ( )
resolver = dns . resolver . Resolver ( )
2023-11-08 18:12:16 +11:00
resolver . nameservers = [ " 10.2.1.15 " ]
2023-11-08 18:09:50 +11:00
resolver . port = 5350
2023-08-11 17:24:11 +10:00
try :
# Query the DNS record
2023-08-11 17:30:07 +10:00
response = resolver . resolve ( domain , record_type )
2023-08-11 17:24:11 +10:00
records = " "
for record in response :
records = records + " \n " + str ( record )
# Send the result to the Discord channel
await ctx . response . send_message ( f " DNS records for { domain } ( { record_type } ): { records } " )
except dns . resolver . NXDOMAIN :
await ctx . response . send_message ( f " Domain { domain } not found. " )
except dns . exception . DNSException as e :
await ctx . response . send_message ( f " An error occurred: { e } " )
2023-08-12 15:18:21 +10:00
@tree.command ( name = " curl " , description = " HTTP request " )
async def curl ( ctx , url : str ) :
try :
proxyURL = " https://proxy.hnsproxy.au "
response = requests . get ( url , proxies = { " http " : proxyURL , " https " : proxyURL } , verify = False )
output = response . text
2023-08-12 22:39:36 +10:00
# Get BODY only
output = output . split ( " <body " ) [ 1 ]
output = output . split ( " </body> " ) [ 0 ]
output = output . split ( " > " , 1 ) [ 1 ]
# Replace any relative links with absolute links
output = output . replace ( ' href= " / ' , ' href= " ' + url + ' / ' )
parsed = markdownify . markdownify ( output , heading_style = " ATX " )
# Delete any empty lines
parsed = " \n " . join ( [ s for s in parsed . splitlines ( ) if s . strip ( ) ] )
output = response . text
# Get title
if ( output . find ( " <title> " ) != - 1 ) :
title = output . split ( " <title> " ) [ 1 ]
title = title . split ( " </title> " ) [ 0 ]
else :
title = url
if ( len ( parsed ) > 4096 ) :
parsed = parsed [ : 4096 ]
# Delete any incomplete lines
parsed = " \n " . join ( parsed . splitlines ( ) [ : - 1 ] )
parsed = parsed + " \n ... "
# if url is a tld only replace it with the url https://hns.au (due to Discord not allowing tld only links)
if ( url . find ( " . " ) == - 1 ) :
url = " https://hns.au "
# Create an embed
embed = discord . Embed ( title = title , url = url , description = parsed )
embed . set_footer ( text = " Parsed by HNSProxy " , icon_url = " https://hns.au/assets/img/favicon.png " )
embed . timestamp = discord . utils . utcnow ( )
await ctx . response . send_message ( embed = embed )
2023-08-12 15:18:21 +10:00
except requests . exceptions . RequestException as e :
await ctx . response . send_message ( f " An error occurred: { e } " )
2023-08-12 22:39:36 +10:00
except Exception as e :
await ctx . response . send_message ( f " An error occurred: { e } " )
2023-09-27 17:27:06 +10:00
@tree.command ( name = " ssl " , description = " Check SSL certificate " )
2023-10-01 22:38:31 +11:00
async def ssl ( ctx , domain : str , showcert : bool = False , notifymeonexpiry : bool = False ) :
2023-09-27 17:35:01 +10:00
# Verify that the domain is valid
if not domain :
await ctx . response . send_message ( " Please provide a domain to check " )
return
2023-09-27 17:27:06 +10:00
2023-09-27 17:35:01 +10:00
regexmatch = re . match ( r " ^([a-z0-9]+(-[a-z0-9]+)* \ .)*([a-z0-9]+(-[a-z0-9]+)*)$ " , domain )
if not regexmatch :
await ctx . response . send_message ( " Please provide a valid domain to check " )
return
2023-09-27 17:27:06 +10:00
2023-09-27 18:18:35 +10:00
await ctx . response . send_message ( f " Checking SSL certificate for { domain } ... " )
2023-09-27 17:27:06 +10:00
message = " "
resolver = dns . resolver . Resolver ( )
2023-11-08 18:12:16 +11:00
resolver . nameservers = [ " 10.2.1.15 " ]
2023-11-08 18:09:50 +11:00
resolver . port = 5350
2023-09-27 18:34:24 +10:00
domain_check = False
2023-09-27 17:27:06 +10:00
try :
# Query the DNS record
response = resolver . resolve ( domain , " A " )
records = [ ]
message = " ## A records: \n "
for record in response :
records . append ( str ( record ) )
message = message + " - " + str ( record ) + " \n "
2023-09-27 17:36:22 +10:00
if not records :
2023-09-27 18:14:34 +10:00
await ctx . channel . send ( f " No A record found for { domain } " )
2023-09-27 17:27:06 +10:00
return
# Get the first A record
ip = records [ 0 ]
# Run the openssl s_client command
2023-09-27 17:31:22 +10:00
s_client_command = [ " openssl " , " s_client " , " -showcerts " , " -connect " , f " { ip } :443 " , " -servername " , domain , ]
2023-09-27 17:27:06 +10:00
2023-09-27 17:31:22 +10:00
s_client_process = subprocess . Popen ( s_client_command , stdout = subprocess . PIPE , stderr = subprocess . PIPE , stdin = subprocess . PIPE )
s_client_output , _ = s_client_process . communicate ( input = b " \n " )
2023-09-27 18:35:27 +10:00
2023-09-27 17:27:06 +10:00
certificates = [ ]
current_cert = " "
for line in s_client_output . split ( b " \n " ) :
current_cert + = line . decode ( " utf-8 " ) + " \n "
if " -----END CERTIFICATE----- " in line . decode ( " utf-8 " ) :
certificates . append ( current_cert )
current_cert = " "
2023-09-27 17:38:56 +10:00
# Remove anything before -----BEGIN CERTIFICATE-----
certificates = [ cert [ cert . find ( " -----BEGIN CERTIFICATE----- " ) : ] for cert in certificates ]
2023-09-27 17:27:06 +10:00
if certificates :
cert = certificates [ 0 ]
2023-10-01 13:32:19 +11:00
if showcert :
message = message + " \n ## Website Certificate: \n ``` \n " + cert + " \n ``` \n "
2023-09-27 17:27:06 +10:00
with tempfile . NamedTemporaryFile ( mode = " w " , delete = False ) as temp_cert_file :
temp_cert_file . write ( cert )
temp_cert_file . seek ( 0 ) # Move back to the beginning of the temporary file
2023-09-27 17:44:07 +10:00
tlsa_command = [ " openssl " , " x509 " , " -in " , temp_cert_file . name , " -pubkey " , " -noout " , " | " , " openssl " , " pkey " , " -pubin " , " -outform " , " der " , " | " , " openssl " , " dgst " , " -sha256 " , " -binary " , ]
2023-09-27 17:27:06 +10:00
tlsa_process = subprocess . Popen ( " " . join ( tlsa_command ) , shell = True , stdout = subprocess . PIPE )
tlsa_output , _ = tlsa_process . communicate ( )
2023-09-27 17:44:07 +10:00
2023-09-27 18:03:53 +10:00
tlsa_server = " 3 1 1 " + binascii . hexlify ( tlsa_output ) . decode ( " utf-8 " )
2023-09-27 17:44:07 +10:00
2023-09-27 18:03:53 +10:00
message = message + " \n ## TLSA Record from webserver: \n ` " + tlsa_server + " ` \n "
2023-09-27 18:25:58 +10:00
# Get domains
2023-09-27 18:29:09 +10:00
cert_obj = x509 . load_pem_x509_certificate ( cert . encode ( " utf-8 " ) , default_backend ( ) )
2023-09-27 18:25:58 +10:00
domains = [ ]
2023-09-27 18:29:09 +10:00
for ext in cert_obj . extensions :
if ext . oid == x509 . ExtensionOID . SUBJECT_ALTERNATIVE_NAME :
san_list = ext . value . get_values_for_type ( x509 . DNSName )
domains . extend ( san_list )
# Extract the common name (CN) from the subject
common_name = cert_obj . subject . get_attributes_for_oid ( x509 . NameOID . COMMON_NAME )
if common_name :
2023-09-27 18:34:24 +10:00
if common_name [ 0 ] . value not in domains :
domains . append ( common_name [ 0 ] . value )
2023-09-27 18:29:09 +10:00
2023-09-27 18:25:58 +10:00
if domains :
message = message + " \n ## SSL Domains: \n "
2023-09-27 18:37:48 +10:00
for cn in domains :
message = message + " - " + cn + " \n "
2023-09-27 18:34:24 +10:00
if domain in domains :
domain_check = True
2023-09-27 18:25:58 +10:00
2023-10-01 13:13:55 +11:00
expiry_date = cert_obj . not_valid_after
2023-10-01 13:20:04 +11:00
# Check if expiry date is past
if expiry_date < datetime . datetime . now ( ) :
message = message + " \n ## Expiry Date: \n :x: Certificate has expired \n "
elif expiry_date < datetime . datetime . now ( ) + datetime . timedelta ( days = 7 ) :
message = message + " \n ## Expiry Date: \n :warning: Certificate expires soon \n "
else :
message = message + " \n ## Expiry Date: \n :white_check_mark: Certificate is valid \n "
message = message + expiry_date . strftime ( " %d % B % Y % H: % M: % S " ) + " \n "
2023-10-01 13:13:55 +11:00
2023-09-27 18:25:58 +10:00
2023-09-27 18:11:27 +10:00
else :
message = message + " \n ## Website Certificate: \n :x: No certificate found \n "
message = message + " \n ## TLSA Record from webserver: \n :x: No certificate found \n "
2023-09-27 17:47:57 +10:00
2023-09-27 18:11:27 +10:00
try :
2023-09-27 17:47:57 +10:00
# Check for TLSA record
response = resolver . resolve ( " _443._tcp. " + domain , " TLSA " )
tlsa_records = [ ]
2023-09-27 18:06:22 +10:00
message = message + " \n ## TLSA Records from DNS: \n "
2023-09-27 17:47:57 +10:00
for record in response :
tlsa_records . append ( str ( record ) )
message = message + " - " + str ( record ) + " \n "
if not tlsa_records :
2023-09-27 17:57:45 +10:00
message = message + " \n ## Result: \n :x: No TLSA record found \n "
else :
if tlsa_server == tlsa_records [ 0 ] :
2023-09-27 18:34:24 +10:00
if domain_check :
message = message + " \n ## Result: \n :white_check_mark: TLSA record matches certificate \n "
else :
message = message + " \n ## Result: \n :x: TLSA record matches certificate but domain does not match \n "
2023-09-27 17:57:45 +10:00
else :
message = message + " \n ## Result: \n :x: TLSA record does not match certificate \n "
2023-09-27 18:11:27 +10:00
except :
message = message + " \n ## TLSA Records from DNS: \n :x: No TLSA record found \n "
message = message + " \n ## Result: \n :x: No TLSA record found \n "
# If message is too long, send it in 2 messages
if ( len ( message ) > 2000 ) :
# Split on the last line under 2000 characters
message1 = message [ : 2000 ]
message1 = message1 [ : message1 . rfind ( " \n " ) ]
message2 = message [ len ( message1 ) : ]
2023-09-27 18:14:34 +10:00
await ctx . channel . send ( message1 )
2023-09-27 18:11:27 +10:00
await ctx . channel . send ( message2 )
2023-09-27 17:27:06 +10:00
else :
2023-09-27 18:14:34 +10:00
await ctx . channel . send ( message )
2023-10-01 22:38:31 +11:00
if ( notifymeonexpiry ) :
with open ( " /mnt/sslnotify.txt " , " a " ) as file :
file . write ( str ( ctx . user . id ) + " , " + domain + " \n " )
2023-09-27 17:27:06 +10:00
# Catch all exceptions
except Exception as e :
2023-09-27 18:14:34 +10:00
await ctx . channel . send ( f " An error occurred: { e } " )
2023-09-27 17:27:06 +10:00
2023-08-11 17:24:11 +10:00
2023-08-12 15:38:32 +10:00
@tree.command ( name = " invite " , description = " Invite me to your server " )
async def invite ( ctx ) :
await ctx . response . send_message ( " https://discord.com/api/oauth2/authorize?client_id=1006128164218621972&permissions=0&scope=bot " , ephemeral = True )
2023-08-11 17:24:11 +10:00
2023-08-11 16:55:18 +10:00
async def log ( message ) :
2023-08-12 16:52:01 +10:00
channel = client . get_channel ( LOG_CHANNEL )
2023-08-11 16:55:18 +10:00
await channel . send ( message )
2023-08-11 15:55:44 +10:00
def updateStatus ( ) :
2023-08-11 16:35:36 +10:00
global activityMessage
global statusType
if ( statusType == " watching " ) :
activity = discord . Activity ( type = discord . ActivityType . watching , name = activityMessage )
elif ( statusType == " playing " ) :
activity = discord . Activity ( type = discord . ActivityType . playing , name = activityMessage )
elif ( statusType == " listening " ) :
activity = discord . Activity ( type = discord . ActivityType . listening , name = activityMessage )
elif ( statusType == " competing " ) :
activity = discord . Activity ( type = discord . ActivityType . competing , name = activityMessage )
else :
activity = discord . Activity ( type = discord . ActivityType . watching , name = activityMessage )
2023-08-11 15:55:44 +10:00
client . loop . create_task ( client . change_presence ( activity = activity ) )
2023-11-14 13:56:55 +11:00
@tasks.loop ( hours = 24 )
2023-10-01 22:53:54 +11:00
async def checkForSSLExpiry ( ) :
2023-10-01 22:38:31 +11:00
with open ( " /mnt/sslnotify.txt " , " r " ) as file :
lines = file . readlines ( )
for line in lines :
line = line . strip ( )
if not line :
2023-10-01 23:12:09 +11:00
print ( " No line " , flush = True )
2023-10-01 22:38:31 +11:00
continue
userid , domain = line . split ( " , " )
2023-10-01 23:12:09 +11:00
print ( f " Checking SSL certificate for { domain } ... " , flush = True )
2023-10-01 22:38:31 +11:00
resolver = dns . resolver . Resolver ( )
2023-11-08 18:09:50 +11:00
resolver . nameservers = [ " 10.2.1.15 " ]
resolver . port = 5350
2023-10-01 22:38:31 +11:00
try :
# Query the DNS record
response = resolver . resolve ( domain , " A " )
records = [ ]
for record in response :
records . append ( str ( record ) )
if not records :
2023-10-01 23:08:01 +11:00
print ( f " No A record found for { domain } " , flush = True )
2023-10-01 22:38:31 +11:00
continue
# Get the first A record
ip = records [ 0 ]
# Run the openssl s_client command
s_client_command = [ " openssl " , " s_client " , " -showcerts " , " -connect " , f " { ip } :443 " , " -servername " , domain , ]
s_client_process = subprocess . Popen ( s_client_command , stdout = subprocess . PIPE , stderr = subprocess . PIPE , stdin = subprocess . PIPE )
s_client_output , _ = s_client_process . communicate ( input = b " \n " )
certificates = [ ]
current_cert = " "
for line in s_client_output . split ( b " \n " ) :
current_cert + = line . decode ( " utf-8 " ) + " \n "
if " -----END CERTIFICATE----- " in line . decode ( " utf-8 " ) :
certificates . append ( current_cert )
current_cert = " "
# Remove anything before -----BEGIN CERTIFICATE-----
certificates = [ cert [ cert . find ( " -----BEGIN CERTIFICATE----- " ) : ] for cert in certificates ]
if certificates :
cert = certificates [ 0 ]
# Get expiry date
cert_obj = x509 . load_pem_x509_certificate ( cert . encode ( " utf-8 " ) , default_backend ( ) )
expiry_date = cert_obj . not_valid_after
if expiry_date < datetime . datetime . now ( ) + datetime . timedelta ( days = 7 ) :
2023-10-01 23:22:45 +11:00
user = await client . fetch_user ( int ( userid ) )
2023-10-01 22:38:31 +11:00
if user :
2023-10-01 22:53:54 +11:00
await user . send ( f " SSL certificate for { domain } expires soon " )
2023-10-01 23:03:01 +11:00
print ( f " SSL certificate for { domain } expires soon " , flush = True )
2023-10-01 23:14:37 +11:00
else :
2023-10-01 23:28:11 +11:00
await log ( f " User { userid } not found " )
2023-10-01 23:08:01 +11:00
else :
print ( f " No certificate found for { domain } " , flush = True )
await user . send ( f " No certificate found for { domain } " )
continue
2023-10-01 22:47:23 +11:00
except Exception as e :
print ( e , flush = True )
2023-10-01 23:28:11 +11:00
await log ( e )
2023-10-01 22:38:31 +11:00
continue
2023-10-01 22:47:23 +11:00
print ( " SSL check complete " , flush = True )
2023-10-01 22:38:31 +11:00
@tree.command ( name = " ssldomains " , description = " List domains with SSL certificates " )
async def ssldomains ( ctx ) :
# Get user id
userid = str ( ctx . user . id )
# Get all domains for user
domains = [ ]
with open ( " /mnt/sslnotify.txt " , " r " ) as file :
lines = file . readlines ( )
for line in lines :
line = line . strip ( )
if not line :
continue
if line . startswith ( userid ) :
_ , domain = line . split ( " , " )
domains . append ( domain )
if not domains :
await ctx . response . send_message ( " You have no domains in the SSL expiry notification list " , ephemeral = True )
return
await ctx . response . send_message ( " Domains in the SSL expiry notification list: \n " + " \n " . join ( domains ) , ephemeral = True )
2023-10-01 22:59:40 +11:00
@tree.command ( name = " sslremove " , description = " Remove a domain from the SSL expiry notification list " )
async def sslremove ( ctx , domain : str ) :
# Get user id
userid = str ( ctx . user . id )
# Get all domains for user
domains = [ ]
with open ( " /mnt/sslnotify.txt " , " r " ) as file :
lines = file . readlines ( )
for line in lines :
line = line . strip ( )
if not line :
continue
if line . startswith ( userid ) :
_ , dom = line . split ( " , " )
domains . append ( dom )
if not domains :
await ctx . response . send_message ( " You have no domains in the SSL expiry notification list " , ephemeral = True )
return
if domain not in domains :
await ctx . response . send_message ( " Domain not found in the SSL expiry notification list " , ephemeral = True )
return
with open ( " /mnt/sslnotify.txt " , " w " ) as file :
for line in lines :
line = line . strip ( )
if not line :
continue
if not line . startswith ( userid ) :
file . write ( line + " \n " )
for dom in domains :
if domain != dom :
file . write ( userid + " , " + dom + " \n " )
await ctx . response . send_message ( " Domain removed from the SSL expiry notification list " , ephemeral = True )
2023-10-01 22:38:31 +11:00
2023-10-01 22:59:40 +11:00
@tree.command ( name = " manualsslcheck " , description = " Manually check SSL certificate " )
async def manualsslcheck ( ctx ) :
if ( ctx . user . id != ADMINID ) :
await log ( " User: " + str ( ctx . user . name ) + " tried to use the manualsslcheck command " )
await ctx . response . send_message ( " You don ' t have permission to use this command " , ephemeral = True )
2023-10-01 22:38:31 +11:00
2023-10-01 22:59:40 +11:00
await ctx . response . send_message ( " SSL checking " , ephemeral = True )
await checkForSSLExpiry ( )
2023-10-01 23:06:55 +11:00
await ctx . channel . send ( " SSL check complete " , ephemeral = True )
2023-10-01 22:59:40 +11:00
2023-11-13 20:11:49 +11:00
@tree.command ( name = " ai " , description = " AI Chat " )
async def ai ( ctx , message : str ) :
prompt = " Name: " + str ( ctx . user . name ) + " \n "
prompt = prompt + " Message: " + message + " \n "
2023-11-13 20:16:01 +11:00
await ctx . response . send_message ( chatai . chat ( prompt ) )
2023-10-01 22:38:31 +11:00
2023-11-14 13:05:41 +11:00
@tree.command ( name = " remindme " , description = " Remind me " )
2023-11-14 13:15:56 +11:00
async def remindme ( ctx , when : str , reminder : str ) :
2023-11-14 13:05:41 +11:00
time_delta = parse_time ( when )
if time_delta is not None :
# Schedule the reminder
2023-11-14 13:15:56 +11:00
reminder_time = datetime . datetime . now ( ) + time_delta
2023-11-14 13:19:25 +11:00
store_reminder ( ctx . user . id , reminder_time , reminder )
2023-11-14 13:15:56 +11:00
await ctx . response . send_message ( " I ' ve set a reminder for you in " + when + " . \n " + reminder , ephemeral = True )
2023-11-14 13:05:41 +11:00
else :
await ctx . response . send_message ( " Invalid time format. Please use something like `1d 3h` or `4hr`. " , ephemeral = True )
2023-11-14 14:05:39 +11:00
@tree.command ( name = " reminders " , description = " List reminders " )
async def reminders ( ctx ) :
reminders = read_reminders ( )
if len ( reminders ) == 0 :
await ctx . response . send_message ( " You have no reminders. " , ephemeral = True )
else :
user_reminders = [ ]
for reminder in reminders :
# Only show reminders for the user who requested them
if reminder [ ' user_id ' ] == str ( ctx . user . id ) :
user_reminders . append ( reminder )
if len ( user_reminders ) == 0 :
await ctx . response . send_message ( " You have no reminders. " , ephemeral = True )
else :
message = " Reminders: \n "
for reminder in user_reminders :
time = datetime . datetime . strptime ( reminder [ ' time ' ] , " % Y- % m- %d % H: % M: % S " )
2023-11-14 14:18:01 +11:00
time = tools . timestamp_relative ( time )
2023-11-14 14:05:39 +11:00
message + = f " { time } : { reminder [ ' text ' ] } \n "
await ctx . response . send_message ( message , ephemeral = True )
2023-11-18 15:20:30 +11:00
@tree.command ( name = " timestamp " , description = " Convert timestamp " )
async def timestamp ( ctx , when : str ) :
when = when . strip ( )
time_delta = parse_time ( when )
if time_delta is not None :
# Schedule the reminder
time = datetime . datetime . now ( ) + time_delta
if when . endswith ( " ago " ) :
time = datetime . datetime . now ( ) - time_delta
time = tools . timestamp_all_raw ( time )
await ctx . response . send_message ( time , ephemeral = True )
else :
await ctx . response . send_message ( " Invalid time format. Please use something like `1d 3h` or `4hr`. End with `ago` to convert to past time " , ephemeral = True )
2023-11-14 13:56:55 +11:00
@tasks.loop ( seconds = 10 )
2023-11-14 13:15:56 +11:00
async def check_reminders ( ) :
now = datetime . datetime . now ( )
reminders = read_reminders ( )
for reminder in reminders [ : ] :
reminder_time = datetime . datetime . strptime ( reminder [ ' time ' ] , " % Y- % m- %d % H: % M: % S " )
if reminder_time < = now :
user = await client . fetch_user ( int ( reminder [ ' user_id ' ] ) )
2023-11-14 13:56:55 +11:00
2023-11-14 14:01:29 +11:00
await user . send ( embed = tools . embed ( " Reminder " , reminder [ ' text ' ] ) )
2023-11-14 13:56:55 +11:00
print ( " Reminder sent for " + str ( reminder ) , flush = True )
2023-11-14 13:15:56 +11:00
reminders . remove ( reminder )
write_reminders ( reminders )
2023-08-11 15:55:44 +10:00
# When the bot is ready
@client.event
async def on_ready ( ) :
2023-08-12 17:14:55 +10:00
global ADMINID
ADMINID = client . application . owner . id
2023-08-11 15:55:44 +10:00
await tree . sync ( )
updateStatus ( )
2023-11-14 13:56:55 +11:00
check_reminders . start ( )
checkForSSLExpiry . start ( )
2023-08-11 15:55:44 +10:00
2023-11-14 13:56:55 +11:00
client . run ( TOKEN )
2023-10-01 22:38:31 +11:00
2023-10-01 22:45:21 +11:00