Compare commits
108 Commits
main
...
develop_ch
Author | SHA1 | Date | |
---|---|---|---|
81f91df8ac | |||
6c6c2655f2 | |||
6012351b3e | |||
934fa4d37f | |||
44982fd439 | |||
46fdebd503 | |||
c294489a60 | |||
2cf2ec7d88 | |||
2f03a0de4c | |||
3b33bb6ee4 | |||
f7e0c4b15a | |||
9fc901a2ef | |||
b7283c17b6 | |||
40f6ea7cd7 | |||
cb94d94943 | |||
00b834c40b | |||
a294b8c668 | |||
263405141b | |||
089bb99119 | |||
2f51c18a69 | |||
7afbb169e2 | |||
bdb9d4b807 | |||
ed67193761 | |||
18c9be444d | |||
40956e5c9e | |||
4de56c1af8 | |||
7b9a7fb339 | |||
3039bf65bd | |||
850e7288d1 | |||
3bbba3217a | |||
7b0fde230d | |||
511f4bf69e | |||
78f1232f5b | |||
41683fe098 | |||
61b617db04 | |||
740213712a | |||
8cdd7d3974 | |||
e7681bf7cd | |||
7bee11e4f4 | |||
2954d1b304 | |||
4868f6930e | |||
42f04a552a | |||
00de13acd2 | |||
9a5942facc | |||
bdc0ed0fad | |||
55490c4b04 | |||
66cec55bb6 | |||
16b41fad2b | |||
ab3ef81df9 | |||
0f68356254 | |||
855a91e340 | |||
8ef2ede2c0 | |||
32954a2536 | |||
a689783b36 | |||
9b0f381673 | |||
1eb4da8729 | |||
85fc4805dc | |||
f7bcbe348b | |||
2307119ad7 | |||
d3daa299c0 | |||
abf8f10668 | |||
acaedb434c | |||
e64811a179 | |||
113f6cbbd5 | |||
7c4b5c52f1 | |||
b5ded6acaf | |||
08a9d0addd | |||
aa1789da97 | |||
9e0c78d939 | |||
d82c569363 | |||
6effe2983e | |||
a729545e01 | |||
7e79b2c3b4 | |||
87be8ca8db | |||
5507fea98e | |||
92df0cf4a7 | |||
944f8d4e72 | |||
27e9fdb743 | |||
b9a699c4c2 | |||
734aa58e67 | |||
dc323ffeb1 | |||
9fd8f3c289 | |||
c82fceeef1 | |||
01a72ca544 | |||
8befc32453 | |||
4a3e470945 | |||
7c6389d421 | |||
ac672e73d0 | |||
43546d93fe | |||
6ea91cd1c9 | |||
0ad30722ae | |||
dfeb3d5148 | |||
6cbf6dc718 | |||
e24cc98fec | |||
a0e979e0bc | |||
69a711a4a4 | |||
89e73e866b | |||
77b526ca21 | |||
881b7f6359 | |||
ece032ff5a | |||
dabb635b6b | |||
2de2202187 | |||
3928b1f564 | |||
8865015499 | |||
811478f486 | |||
808ca6c442 | |||
cb3466c6c6 | |||
0d15e7931c |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
config.json
|
config.json
|
||||||
node_modules
|
node_modules
|
||||||
keyfile.json
|
keyfile.json
|
||||||
|
media
|
@ -14,7 +14,7 @@ I have a subset of features that I want it to have before I call it "production
|
|||||||
- [ ] Add Autocomplete for common elements like Calendar Names, Event Names, Timezones, etc.
|
- [ ] Add Autocomplete for common elements like Calendar Names, Event Names, Timezones, etc.
|
||||||
- [ ] Calendar Announcements for Upcoming Events
|
- [ ] Calendar Announcements for Upcoming Events
|
||||||
- [ ] Poll Creation and Results Announcements
|
- [ ] Poll Creation and Results Announcements
|
||||||
- [ ] Conversation Archiving (May be Removed)
|
- [ ] Conversation Archiving (Partially working, need to work out edited messages, deleted messages, and attachments)
|
||||||
|
|
||||||
"Adventurous (Lots of Work)" Features
|
"Adventurous (Lots of Work)" Features
|
||||||
- [ ] BreadBot Voice Chat Hall Monitor
|
- [ ] BreadBot Voice Chat Hall Monitor
|
||||||
|
201
bin/breadmixer.py
Normal file
201
bin/breadmixer.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
import argparse
|
||||||
|
import mysql.connector
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
import subprocess
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
from txtai.pipeline import Transcription
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
argument_parser = argparse.ArgumentParser(description="BreadMixer is used to combine media from Discord Voice Calls")
|
||||||
|
argument_parser.add_argument("callid", help="The call id that needs to be mixed")
|
||||||
|
argument_parser.add_argument("config", help="The BreadBot config file location")
|
||||||
|
argument_parser.add_argument("-f", "--filespercycle", help="The number of files to combine per run of ffmpeg", default=50)
|
||||||
|
argument_parser.add_argument("-v", "--verbose", help="Make the script tell you more about what it's doing", action="store_true")
|
||||||
|
|
||||||
|
args = argument_parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print("Checking config file")
|
||||||
|
|
||||||
|
if not os.path.exists(args.config):
|
||||||
|
print('The file path {path} does not exist'.format(path=args.config))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(args.config) as config:
|
||||||
|
json_config = json.loads(config.read())
|
||||||
|
|
||||||
|
config_must_contain = [
|
||||||
|
"mysql_username",
|
||||||
|
"mysql_password",
|
||||||
|
"mysql_db_name",
|
||||||
|
"mysql_host",
|
||||||
|
"media_voice_folder"
|
||||||
|
]
|
||||||
|
|
||||||
|
if not all([element in json_config for element in config_must_contain]):
|
||||||
|
print('One or more of the following config items are missing')
|
||||||
|
for element in config_must_contain:
|
||||||
|
print('\t{item}'.format(item=element))
|
||||||
|
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print("Connecting to mysql db {dbname}".format(dbname=json_config["mysql_db_name"]))
|
||||||
|
|
||||||
|
mydb = mysql.connector.connect(
|
||||||
|
host=json_config["mysql_host"],
|
||||||
|
user=json_config["mysql_username"],
|
||||||
|
password=json_config["mysql_password"],
|
||||||
|
database=json_config["mysql_db_name"]
|
||||||
|
)
|
||||||
|
mydb.autocommit = True
|
||||||
|
|
||||||
|
cursor = mydb.cursor()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print("Checking to see if call ID {callid} exists".format(callid=args.callid))
|
||||||
|
|
||||||
|
cursor.execute("SELECT call_start_time FROM call_states WHERE call_id = %s", [args.callid])
|
||||||
|
|
||||||
|
call_start_time = cursor.fetchall()
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
if len(call_start_time) == 0:
|
||||||
|
print('{call_id} does not exist in the database'.format(call_id=args.callid))
|
||||||
|
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print("Collecting files from {folder}".format(folder=os.path.join(json_config["media_voice_folder"], args.callid)))
|
||||||
|
|
||||||
|
file_dict = {}
|
||||||
|
|
||||||
|
for file in os.listdir(os.path.join(json_config["media_voice_folder"], args.callid)):
|
||||||
|
file_name_no_ext = file.split('.')[0]
|
||||||
|
timestamp = int(file_name_no_ext.split('-')[0])
|
||||||
|
user_snowflake = file_name_no_ext.split('-')[1]
|
||||||
|
|
||||||
|
file_stamp_as_datetime = datetime.datetime.fromtimestamp(timestamp / 1000)
|
||||||
|
time_diff = file_stamp_as_datetime - call_start_time[0][0]
|
||||||
|
|
||||||
|
file_dict[os.path.join(json_config["media_voice_folder"], args.callid, file)] = dict(
|
||||||
|
user=user_snowflake,
|
||||||
|
real_date=file_stamp_as_datetime,
|
||||||
|
milliseconds_from_starttime=int((time_diff.seconds * 1000) + (time_diff.microseconds / 1000))
|
||||||
|
)
|
||||||
|
|
||||||
|
file_dict_items = [(k, v) for k, v in file_dict.items()]
|
||||||
|
file_dict_items.sort(key=lambda a: a[1]["milliseconds_from_starttime"])
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print("Collected files: ")
|
||||||
|
[print(element) for element in file_dict_items]
|
||||||
|
|
||||||
|
list_of_final_merges = []
|
||||||
|
|
||||||
|
for i in range(0, len(file_dict_items), args.filespercycle):
|
||||||
|
input_list = []
|
||||||
|
filter_list = []
|
||||||
|
|
||||||
|
next_endpoint = i + 50 if i + 50 <= len(file_dict_items) else len(file_dict_items)
|
||||||
|
|
||||||
|
for j in range(i, next_endpoint, 1):
|
||||||
|
input_list.append(file_dict_items[j][0])
|
||||||
|
filter_list.append("[{inputid}]adelay={delay}|{delay}[a{inputid}]".format(
|
||||||
|
inputid = j - i,
|
||||||
|
delay = file_dict_items[j][1]["milliseconds_from_starttime"]
|
||||||
|
))
|
||||||
|
|
||||||
|
command_list = ["ffmpeg"]
|
||||||
|
|
||||||
|
for input in input_list:
|
||||||
|
command_list.append("-i")
|
||||||
|
command_list.append(input)
|
||||||
|
|
||||||
|
command_list.append("-filter_complex")
|
||||||
|
|
||||||
|
filter_string = "\""
|
||||||
|
filter_string = filter_string + ';'.join(filter_list)
|
||||||
|
filter_string = filter_string + ";"
|
||||||
|
|
||||||
|
for j in range(i, next_endpoint, 1):
|
||||||
|
filter_string = filter_string + "[a{inputid}]".format(inputid=j - i)
|
||||||
|
|
||||||
|
filter_string = filter_string + "amix=inputs={input_count}:normalize=0[a]\"".format(input_count = next_endpoint - i)
|
||||||
|
|
||||||
|
command_list.append(filter_string)
|
||||||
|
command_list.append("-map")
|
||||||
|
command_list.append("\"[a]\"")
|
||||||
|
|
||||||
|
output_file_name = os.path.join(
|
||||||
|
json_config["media_voice_folder"],
|
||||||
|
args.callid,
|
||||||
|
"intermediate-" + "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) + ".mp3"
|
||||||
|
)
|
||||||
|
|
||||||
|
list_of_final_merges.append(output_file_name)
|
||||||
|
|
||||||
|
command_list.append(output_file_name)
|
||||||
|
|
||||||
|
ffmpeg_process = subprocess.Popen(' '.join(command_list), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||||
|
print(ffmpeg_process.args)
|
||||||
|
|
||||||
|
stdout, stderr = ffmpeg_process.communicate()
|
||||||
|
|
||||||
|
if (ffmpeg_process.returncode != 0):
|
||||||
|
print("The ffmpeg process failed")
|
||||||
|
print(stdout)
|
||||||
|
print(stderr)
|
||||||
|
sys.exit(5)
|
||||||
|
|
||||||
|
final_command_list = ["ffmpeg"]
|
||||||
|
|
||||||
|
for file in list_of_final_merges:
|
||||||
|
final_command_list.append("-i")
|
||||||
|
final_command_list.append(file)
|
||||||
|
|
||||||
|
final_command_list.append("-filter_complex")
|
||||||
|
|
||||||
|
filter_string = "\""
|
||||||
|
|
||||||
|
for i in range(len(list_of_final_merges)):
|
||||||
|
filter_string = filter_string + "[{inputid}]".format(inputid=i)
|
||||||
|
|
||||||
|
filter_string = filter_string + "amix=inputs={input_count}:normalize=0[a];[a]volume=3[boosted]\"".format(input_count=len(list_of_final_merges))
|
||||||
|
|
||||||
|
final_command_list.append(filter_string)
|
||||||
|
final_command_list.append("-map")
|
||||||
|
final_command_list.append("\"[boosted]\"")
|
||||||
|
final_command_list.append(os.path.join(json_config["media_voice_folder"], args.callid, "output.mp3"))
|
||||||
|
|
||||||
|
final_command_process = subprocess.Popen(' '.join(final_command_list), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||||
|
stdout, stderr = final_command_process.communicate()
|
||||||
|
|
||||||
|
if (final_command_process.returncode != 0):
|
||||||
|
print("The final ffmpeg process failed")
|
||||||
|
print(stdout)
|
||||||
|
print(stderr)
|
||||||
|
sys.exit(6)
|
||||||
|
|
||||||
|
for file in os.listdir(os.path.join(json_config["media_voice_folder"], args.callid)):
|
||||||
|
if file.startswith("intermediate"):
|
||||||
|
os.remove(os.path.join(json_config["media_voice_folder"], args.callid, file))
|
||||||
|
|
||||||
|
transcribe = Transcription("openai/whisper-base")
|
||||||
|
|
||||||
|
for (k, v) in file_dict.items():
|
||||||
|
text = transcribe(k)
|
||||||
|
|
||||||
|
cursor = mydb.cursor()
|
||||||
|
cursor.execute("INSERT INTO call_transcriptions (call_id, user_snowflake, speaking_start_time, text) VALUES (%s, %s, %s, %s)", [
|
||||||
|
args.callid,
|
||||||
|
v["user"],
|
||||||
|
v["real_date"],
|
||||||
|
text
|
||||||
|
])
|
||||||
|
cursor.close()
|
264
breadbot.js
264
breadbot.js
@ -1,7 +1,33 @@
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { Client, Events, GatewayIntentBits, Collection } = require('discord.js');
|
const { Client, Events, GatewayIntentBits, Collection } = require('discord.js');
|
||||||
const { token } = require('./config.json');
|
const { joinVoiceChannel, getVoiceConnection, entersState, VoiceConnectionStatus, EndBehaviorType } = require('@discordjs/voice')
|
||||||
|
const { token, media_voice_folder, breadbot_logging_config } = require('./config.json');
|
||||||
|
const winston = require('winston')
|
||||||
|
const winston_mysql = require('winston-mysql')
|
||||||
|
const sqlutil = require('./utilities/sqlutil');
|
||||||
|
const { Console } = require('node:console');
|
||||||
|
const prism = require('prism-media')
|
||||||
|
|
||||||
|
const logger = winston.createLogger({
|
||||||
|
level: "silly",
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.simple(),
|
||||||
|
level: breadbot_logging_config["console_log_level"]
|
||||||
|
}),
|
||||||
|
new winston_mysql({
|
||||||
|
level: breadbot_logging_config["sql_log_level"],
|
||||||
|
host: breadbot_logging_config["mysql_host"],
|
||||||
|
user: breadbot_logging_config["mysql_username"],
|
||||||
|
password: breadbot_logging_config["mysql_password"],
|
||||||
|
database: breadbot_logging_config["mysql_db_name"],
|
||||||
|
table: breadbot_logging_config["mysql_table_name"]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
sqlutil.buildPool()
|
||||||
|
|
||||||
const getAllFiles = function(directoryPath, arrayOfFiles) {
|
const getAllFiles = function(directoryPath, arrayOfFiles) {
|
||||||
const files = fs.readdirSync(directoryPath);
|
const files = fs.readdirSync(directoryPath);
|
||||||
@ -20,26 +46,25 @@ const getAllFiles = function(directoryPath, arrayOfFiles) {
|
|||||||
return arrayOfFiles;
|
return arrayOfFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allFiles = [];
|
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildVoiceStates] });
|
||||||
getAllFiles('.' + path.sep + 'commands', allFiles);
|
|
||||||
|
|
||||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
||||||
|
|
||||||
client.commands = new Collection();
|
client.commands = new Collection();
|
||||||
|
|
||||||
const commandFiles = allFiles.filter(file => file.endsWith('.js'));
|
var activeCalls = []
|
||||||
|
|
||||||
for (const file of commandFiles) {
|
getAllFiles('.' + path.sep + 'commands', [])
|
||||||
const command = require(file);
|
.filter(file => file.endsWith('.js'))
|
||||||
|
.forEach(file => {
|
||||||
|
const command = require(file);
|
||||||
|
|
||||||
if ('data' in command && 'execute' in command) {
|
if ('enabled' in command && command.enabled && 'data' in command && 'execute' in command) {
|
||||||
client.commands.set(command.data.name, command);
|
client.commands.set(command.data.name, command);
|
||||||
console.log(`[INFO] Loaded command at ${file}`);
|
logger.info(`Loaded command at ${file}`)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(`[WARNING] The command at ${file} is missing a required "data" or "execute" property.`);
|
logger.warn(`The command at ${file} is missing a required "data" or "execute" property or is not enabled`)
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
client.on(Events.InteractionCreate, async interaction => {
|
client.on(Events.InteractionCreate, async interaction => {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
@ -47,7 +72,7 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
const command = interaction.client.commands.get(interaction.commandName);
|
const command = interaction.client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
if (!command) {
|
if (!command) {
|
||||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
logger.error(`No command matching ${interaction.commandName} was found`)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,13 +80,214 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
await command.execute(interaction);
|
await command.execute(interaction);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(error);
|
logger.error(error)
|
||||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on(Events.GuildCreate, async guild => {
|
||||||
|
if (guild.available) {
|
||||||
|
logger.info(`Got into a server, ${guild.name}, ${guild.id}, ${guild.description}`)
|
||||||
|
|
||||||
|
sqlutil.registerServerIfMissing(guild.id, guild.name, guild.description).then(server_added => {
|
||||||
|
if(server_added) {
|
||||||
|
logger.info(`Server Added ${guild.name}`)
|
||||||
|
} else {
|
||||||
|
logger.error(`Server failed to add ${guild.name}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on(Events.VoiceStateUpdate, async (oldState, newState) => {
|
||||||
|
logger.info("Voice State Update Fired")
|
||||||
|
|
||||||
|
if (oldState.channel == null && newState.channel != null) {
|
||||||
|
if (newState.member.id == client.user.id) {
|
||||||
|
return //If the user is breadbot, ignore and exit
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Channel Join Detected ${newState.guild.id} - ${newState.channelId} - ${newState.member.id}`)
|
||||||
|
|
||||||
|
//TODO Nothing should happen after this if the channel fails to register
|
||||||
|
await sqlutil.registerChannelIfMissing(newState.channel.id, newState.guild.id, newState.channel.name)
|
||||||
|
|
||||||
|
var existingCallID = await sqlutil.inCall(newState.guild.id, newState.channelId)
|
||||||
|
|
||||||
|
logger.info(`Existing call ID ${existingCallID}`)
|
||||||
|
|
||||||
|
if (existingCallID == -1) {
|
||||||
|
logger.info("Joining a call")
|
||||||
|
|
||||||
|
var newCallID = await sqlutil.registerNewCall(newState.guild.id, newState.channelId, new Date())
|
||||||
|
existingCallID = newCallID // To ensure all the stuff that happens after call creation works
|
||||||
|
|
||||||
|
logger.info(`Next call ID ${newCallID}`)
|
||||||
|
|
||||||
|
fs.mkdirSync(media_voice_folder + path.sep + newCallID, {recursive: true})
|
||||||
|
|
||||||
|
const connection = joinVoiceChannel({
|
||||||
|
channelId: newState.channelId,
|
||||||
|
guildId: newState.guild.id,
|
||||||
|
selfDeaf: false,
|
||||||
|
selfMute: true,
|
||||||
|
adapterCreator: newState.guild.voiceAdapterCreator
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await entersState(connection, VoiceConnectionStatus.Ready, 20e3)
|
||||||
|
const receiver = connection.receiver
|
||||||
|
|
||||||
|
if (receiver.speaking.listenerCount("start") == 0) {
|
||||||
|
receiver.speaking.on("start", (user_id) => {
|
||||||
|
if(!receiver.subscriptions.has(user_id)) {
|
||||||
|
receiver.subscribe(user_id, {
|
||||||
|
end: {
|
||||||
|
behavior: EndBehaviorType.AfterSilence,
|
||||||
|
duration: 500
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.pipe(new prism.opus.OggLogicalBitstream({
|
||||||
|
opusHead: new prism.opus.OpusHead({
|
||||||
|
channelCount: 2,
|
||||||
|
sampleRate: 48000
|
||||||
|
}),
|
||||||
|
pageSizeControl: {
|
||||||
|
maxPackets: 10
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(fs.createWriteStream(media_voice_folder + path.sep + newCallID + path.sep + `${Date.now()}-${user_id}.ogg`))
|
||||||
|
} else {
|
||||||
|
logger.warn(`Attempted to create new user subscriber for ${user_id} even though one already exists, receiver arm if statement protected against this`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
receiver.speaking.on("end", (user_id) => {
|
||||||
|
logger.info(`User ${user_id} stopped speaking`)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logger.warn("Attempted to create a new start and end listener for users who are speaking for some reason, receiver armor if statement protected against this")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var userRegistered = await sqlutil.registerUserIfMissing(newState.member.id, newState.member.username, newState.member.displayName)
|
||||||
|
|
||||||
|
if (userRegistered) {
|
||||||
|
var markedUserInCall = await sqlutil.registerUserInCall(existingCallID, newState.member.id)
|
||||||
|
|
||||||
|
if (!markedUserInCall) {
|
||||||
|
logger.error(`Something went wrong when marking user in voice call: ${newState.member.id} - ${newState.channelId}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error(`Something went wrong when registering user for call: ${newState.member.id} - ${newState.member.username}`)
|
||||||
|
}
|
||||||
|
} else if (oldState.channel != null && newState.channel == null) {
|
||||||
|
if (oldState.member.id == client.user.id) {
|
||||||
|
return //If the user is breadbot, ignore and exit
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Channel Exit Detected ${oldState.guild.id} - ${oldState.channelId} - ${oldState.member.id}`)
|
||||||
|
|
||||||
|
var existingCallID = await sqlutil.inCall(oldState.guild.id, oldState.channelId)
|
||||||
|
|
||||||
|
logger.info(`Existing call ID ${existingCallID}`)
|
||||||
|
|
||||||
|
if (existingCallID != -1) {
|
||||||
|
await sqlutil.deregisterUserInCall(existingCallID, oldState.member.id)
|
||||||
|
|
||||||
|
var usersInCall = await sqlutil.getNumberUsersInCall(existingCallID)
|
||||||
|
|
||||||
|
if (usersInCall == 0) {
|
||||||
|
const connection = getVoiceConnection(oldState.guild.id)
|
||||||
|
connection.disconnect()
|
||||||
|
|
||||||
|
var didUpdateEndTime = await sqlutil.updateCallEndTime(existingCallID, new Date())
|
||||||
|
|
||||||
|
if (!didUpdateEndTime) {
|
||||||
|
logger.error(`Failed to mark call ID ${existingCallID} as ended with an end date`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("Couldn't find a call ID based on the guild and channel info, was Breadbot in the call?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on(Events.MessageCreate, async message => {
|
||||||
|
console.info("Message Create Fired")
|
||||||
|
|
||||||
|
var channel_ok = await sqlutil.registerChannelIfMissing(message.channelId, message.channel.guild.id, message.channel.name)
|
||||||
|
var user_ok = await sqlutil.registerUserIfMissing(message.author.id, message.author.username, message.author.displayName)
|
||||||
|
|
||||||
|
logger.info(`Channel Ok? ${channel_ok} User OK? ${user_ok}`)
|
||||||
|
|
||||||
|
if (channel_ok && user_ok) {
|
||||||
|
await sqlutil.registerMessage(message.id, message.channelId, message.author.id, message.content, message.createdAt).then(async message_add => {
|
||||||
|
if(message_add) {
|
||||||
|
logger.info("Message Added")
|
||||||
|
|
||||||
|
if (message.attachments.size != 0) {
|
||||||
|
const all_attachments = message.attachments.map(attachment => sqlutil.registerAttachmentIfMissing(
|
||||||
|
attachment.id,
|
||||||
|
message.id,
|
||||||
|
attachment.name,
|
||||||
|
attachment.description,
|
||||||
|
message.createdAt,
|
||||||
|
attachment.contentType,
|
||||||
|
attachment.url
|
||||||
|
))
|
||||||
|
|
||||||
|
await Promise.all(all_attachments).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("Failed to log message")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on(Events.MessageUpdate, async (oldMessage, newMessage) => {
|
||||||
|
logger.info("Message Update Fired")
|
||||||
|
logger.info(`Old Message Snowflake: ${oldMessage.id} New Message Snowflake: ${newMessage.id}`)
|
||||||
|
|
||||||
|
var editTime = newMessage.editedAt
|
||||||
|
|
||||||
|
if (editTime == null) {
|
||||||
|
editTime = newMessage.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
await sqlutil.updateMessageContentIfPresent(newMessage.id, newMessage.content, editTime).then(async (updated) => {
|
||||||
|
if (updated) {
|
||||||
|
if (newMessage.attachments.size != 0) {
|
||||||
|
const all_attachments = newMessage.attachments.map(attachment => sqlutil.registerAttachmentIfMissing(
|
||||||
|
attachment.id,
|
||||||
|
newMessage.id,
|
||||||
|
attachment.name,
|
||||||
|
attachment.description,
|
||||||
|
editTime,
|
||||||
|
attachment.contentType,
|
||||||
|
attachment.url
|
||||||
|
))
|
||||||
|
|
||||||
|
await Promise.all(all_attachments).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on(Events.MessageDelete, async deletedMessage => {
|
||||||
|
await sqlutil.markMessageDeletedIfPresent(deletedMessage.id)
|
||||||
|
})
|
||||||
|
|
||||||
client.once(Events.ClientReady, c => {
|
client.once(Events.ClientReady, c => {
|
||||||
console.log(`Ready! Logged in as ${c.user.tag}`);
|
logger.info(`Ready! Logged in as ${c.user.tag} - ${c.user.id}`)
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(token);
|
client.login(token);
|
@ -1,6 +1,7 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
enabled: true,
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('ping')
|
.setName('ping')
|
||||||
.setDescription('Replies with Pong!'),
|
.setDescription('Replies with Pong!'),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
enabled: true,
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('server')
|
.setName('server')
|
||||||
.setDescription('Provides information about the server.'),
|
.setDescription('Provides information about the server.'),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
enabled: true,
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('user')
|
.setName('user')
|
||||||
.setDescription('Provides information about the user.'),
|
.setDescription('Provides information about the user.'),
|
||||||
|
178
database_generator.sql
Normal file
178
database_generator.sql
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
-- --------------------------------------------------------
|
||||||
|
-- Host: 192.168.1.196
|
||||||
|
-- Server version: 8.0.33 - MySQL Community Server - GPL
|
||||||
|
-- Server OS: Linux
|
||||||
|
-- HeidiSQL Version: 12.5.0.6677
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET NAMES utf8 */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
|
||||||
|
-- Dumping database structure for breadbot_test
|
||||||
|
CREATE DATABASE IF NOT EXISTS `breadbot_test` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
|
||||||
|
USE `breadbot_test`;
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.call_states
|
||||||
|
CREATE TABLE IF NOT EXISTS `call_states` (
|
||||||
|
`call_id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`server_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`channel_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`call_start_time` datetime NOT NULL,
|
||||||
|
`call_end_time` datetime DEFAULT NULL,
|
||||||
|
`call_consolidated` bit(1) NOT NULL DEFAULT (0),
|
||||||
|
`call_transcribed` bit(1) NOT NULL DEFAULT (0),
|
||||||
|
`call_data_cleaned_up` bit(1) NOT NULL DEFAULT (0),
|
||||||
|
PRIMARY KEY (`call_id`),
|
||||||
|
KEY `fk_snowflake_recording_to_server` (`server_snowflake`),
|
||||||
|
KEY `fk_snowflake_recording_to_channel` (`channel_snowflake`),
|
||||||
|
CONSTRAINT `fk_snowflake_recording_to_channel` FOREIGN KEY (`channel_snowflake`) REFERENCES `channels` (`channel_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_snowflake_recording_to_server` FOREIGN KEY (`server_snowflake`) REFERENCES `servers` (`server_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.call_transcriptions
|
||||||
|
CREATE TABLE IF NOT EXISTS `call_transcriptions` (
|
||||||
|
`transcription_id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`call_id` int NOT NULL,
|
||||||
|
`user_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`speaking_start_time` datetime NOT NULL,
|
||||||
|
`text` longtext NOT NULL,
|
||||||
|
PRIMARY KEY (`transcription_id`),
|
||||||
|
KEY `fk_call_id_states_to_transcriptions` (`call_id`),
|
||||||
|
KEY `fk_snowflake_call_states_to_users` (`user_snowflake`),
|
||||||
|
CONSTRAINT `fk_call_id_states_to_transcriptions` FOREIGN KEY (`call_id`) REFERENCES `call_states` (`call_id`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_snowflake_call_states_to_users` FOREIGN KEY (`user_snowflake`) REFERENCES `users` (`user_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.call_users
|
||||||
|
CREATE TABLE IF NOT EXISTS `call_users` (
|
||||||
|
`call_users_id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`call_id` int NOT NULL,
|
||||||
|
`user_snowflake` bigint unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (`call_users_id`) USING BTREE,
|
||||||
|
KEY `fk_call_id_call_user_to_state` (`call_id`),
|
||||||
|
KEY `fk_snowflake_call_user_to_users` (`user_snowflake`),
|
||||||
|
CONSTRAINT `fk_call_id_call_user_to_state` FOREIGN KEY (`call_id`) REFERENCES `call_states` (`call_id`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_snowflake_call_user_to_users` FOREIGN KEY (`user_snowflake`) REFERENCES `users` (`user_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.channels
|
||||||
|
CREATE TABLE IF NOT EXISTS `channels` (
|
||||||
|
`channel_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`server_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`channel_name` text,
|
||||||
|
PRIMARY KEY (`channel_snowflake`),
|
||||||
|
KEY `fk_snowflake_channel_to_server` (`server_snowflake`),
|
||||||
|
CONSTRAINT `fk_snowflake_channel_to_server` FOREIGN KEY (`server_snowflake`) REFERENCES `servers` (`server_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.messages
|
||||||
|
CREATE TABLE IF NOT EXISTS `messages` (
|
||||||
|
`message_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`channel_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`user_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`message_content` text NOT NULL,
|
||||||
|
`message_timestamp` datetime NOT NULL,
|
||||||
|
`message_deleted` bit(1) NOT NULL DEFAULT (0),
|
||||||
|
PRIMARY KEY (`message_snowflake`),
|
||||||
|
KEY `fk_snowflake_message_to_channel` (`channel_snowflake`),
|
||||||
|
KEY `fk_snowflake_message_to_user` (`user_snowflake`),
|
||||||
|
CONSTRAINT `fk_snowflake_message_to_channel` FOREIGN KEY (`channel_snowflake`) REFERENCES `channels` (`channel_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_snowflake_message_to_user` FOREIGN KEY (`user_snowflake`) REFERENCES `users` (`user_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.message_attachments
|
||||||
|
CREATE TABLE IF NOT EXISTS `message_attachments` (
|
||||||
|
`attachment_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`message_snowflake` bigint unsigned NOT NULL DEFAULT (0),
|
||||||
|
`attachment_name` text NOT NULL,
|
||||||
|
`attachment_description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
|
||||||
|
`attachment_timestamp` datetime NOT NULL,
|
||||||
|
`attachment_mime_type` tinytext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
|
||||||
|
`attachment_url` text NOT NULL,
|
||||||
|
`attachment_downloaded` bit(1) NOT NULL DEFAULT (0),
|
||||||
|
PRIMARY KEY (`attachment_snowflake`) USING BTREE,
|
||||||
|
KEY `fk_snowflake_messages_to_attachments` (`message_snowflake`),
|
||||||
|
CONSTRAINT `fk_snowflake_messages_to_attachments` FOREIGN KEY (`message_snowflake`) REFERENCES `messages` (`message_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.message_content_changes
|
||||||
|
CREATE TABLE IF NOT EXISTS `message_content_changes` (
|
||||||
|
`message_change_id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`message_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`message_change_old_timestamp` datetime NOT NULL,
|
||||||
|
`message_change_old_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
|
||||||
|
PRIMARY KEY (`message_change_id`),
|
||||||
|
KEY `fk_snowflake_message_to_message_changes` (`message_snowflake`),
|
||||||
|
CONSTRAINT `fk_snowflake_message_to_message_changes` FOREIGN KEY (`message_snowflake`) REFERENCES `messages` (`message_snowflake`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.servers
|
||||||
|
CREATE TABLE IF NOT EXISTS `servers` (
|
||||||
|
`server_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`server_name` text NOT NULL,
|
||||||
|
`server_description` mediumtext,
|
||||||
|
PRIMARY KEY (`server_snowflake`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.users
|
||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`user_snowflake` bigint unsigned NOT NULL,
|
||||||
|
`user_name` text NOT NULL,
|
||||||
|
`user_displayname` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
|
||||||
|
PRIMARY KEY (`user_snowflake`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.winston_breadbot
|
||||||
|
CREATE TABLE IF NOT EXISTS `winston_breadbot` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`level` varchar(16) NOT NULL,
|
||||||
|
`message` varchar(2048) NOT NULL,
|
||||||
|
`meta` varchar(2048) NOT NULL,
|
||||||
|
`timestamp` datetime NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
-- Dumping structure for table breadbot_test.winston_sqlutil
|
||||||
|
CREATE TABLE IF NOT EXISTS `winston_sqlutil` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`level` varchar(16) NOT NULL,
|
||||||
|
`message` varchar(2048) NOT NULL,
|
||||||
|
`meta` varchar(2048) NOT NULL,
|
||||||
|
`timestamp` datetime NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
-- Data exporting was unselected.
|
||||||
|
|
||||||
|
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
||||||
|
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
|
@ -32,7 +32,7 @@ const commandFiles = allFiles.filter(file => file.endsWith('.js'));
|
|||||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||||
for (const file of commandFiles) {
|
for (const file of commandFiles) {
|
||||||
const command = require(`${file}`);
|
const command = require(`${file}`);
|
||||||
if ('data' in command && 'execute' in command) {
|
if ('enabled' in command && command.enabled && 'data' in command && 'execute' in command) {
|
||||||
commands.push(command.data.toJSON());
|
commands.push(command.data.toJSON());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
3012
package-lock.json
generated
3012
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -9,9 +9,18 @@
|
|||||||
"author": "Bradley Bickford",
|
"author": "Bradley Bickford",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@discordjs/opus": "^0.9.0",
|
||||||
|
"@discordjs/voice": "^0.16.0",
|
||||||
"@vvo/tzdb": "^6.77.0",
|
"@vvo/tzdb": "^6.77.0",
|
||||||
"discord.js": "^14.6.0",
|
"discord.js": "^14.6.0",
|
||||||
"googleapis": "^109.0.1"
|
"googleapis": "^109.0.1",
|
||||||
|
"libsodium-wrappers": "^0.7.13",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"mysql2": "^3.6.3",
|
||||||
|
"node-crc": "1.3.2",
|
||||||
|
"prism-media": "^2.0.0-alpha.0",
|
||||||
|
"winston": "^3.11.0",
|
||||||
|
"winston-mysql": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.27.0"
|
"eslint": "^8.27.0"
|
||||||
|
18
promise_chain_test.js
Normal file
18
promise_chain_test.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const mysql = require('mysql2')
|
||||||
|
const { mysql_username, mysql_password } = require('./config.json')
|
||||||
|
|
||||||
|
var connection_pool = mysql.createPool({
|
||||||
|
host: "10.26.48.207",
|
||||||
|
user: mysql_username,
|
||||||
|
password: mysql_password,
|
||||||
|
database: 'breadbot_test',
|
||||||
|
connectionLimit: 10
|
||||||
|
}).promise()
|
||||||
|
|
||||||
|
connection_pool.query("SELECT * FROM servers").then(async ([rows, fields]) => {
|
||||||
|
console.log(rows)
|
||||||
|
return await connection_pool.query("SELECT * FROM channels").then(([rows, fields]) => {
|
||||||
|
console.log(rows)
|
||||||
|
return "SOME TEXT"
|
||||||
|
})
|
||||||
|
}).catch(() => {return "SOME FAIL TEXT"}).then(console.log)
|
241
utilities/sqlutil.js
Normal file
241
utilities/sqlutil.js
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
const mysql = require('mysql2')
|
||||||
|
const winston = require('winston')
|
||||||
|
const winston_mysql = require('winston-mysql')
|
||||||
|
const {
|
||||||
|
mysql_username,
|
||||||
|
mysql_password,
|
||||||
|
mysql_host,
|
||||||
|
mysql_db_name,
|
||||||
|
sqlutil_logging_config
|
||||||
|
} = require('../config.json')
|
||||||
|
|
||||||
|
var connection_pool = null
|
||||||
|
var logger = null
|
||||||
|
|
||||||
|
async function buildPool() {
|
||||||
|
if (connection_pool == null) {
|
||||||
|
connection_pool = mysql.createPool({
|
||||||
|
host: mysql_host,
|
||||||
|
user: mysql_username,
|
||||||
|
password: mysql_password,
|
||||||
|
database: mysql_db_name,
|
||||||
|
connectionLimit: 10,
|
||||||
|
multipleStatements: true
|
||||||
|
}).promise()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger == null) {
|
||||||
|
logger = winston.createLogger({
|
||||||
|
level: "silly",
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.simple(),
|
||||||
|
level: sqlutil_logging_config["console_log_level"]
|
||||||
|
}),
|
||||||
|
new winston_mysql({
|
||||||
|
level: sqlutil_logging_config["sql_log_level"],
|
||||||
|
host: sqlutil_logging_config["mysql_host"],
|
||||||
|
user: sqlutil_logging_config["mysql_username"],
|
||||||
|
password: sqlutil_logging_config["mysql_password"],
|
||||||
|
database: sqlutil_logging_config["mysql_db_name"],
|
||||||
|
table: sqlutil_logging_config["mysql_table_name"]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerServerIfMissing(server_snowflake, server_name, server_description) {
|
||||||
|
return connection_pool.query("SELECT * FROM servers WHERE server_snowflake = ?;", [server_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length != 0) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return await connection_pool.query("INSERT INTO servers VALUES (?, ?, ?)", [server_snowflake, server_name, server_description]).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerChannelIfMissing(channel_snowflake, server_snowflake, channel_name) {
|
||||||
|
return connection_pool.query("SELECT * FROM channels WHERE channel_snowflake = ?;", [channel_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length != 0) {+
|
||||||
|
logger.info("Channel already registered")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
logger.info("Channel Not registered, registering")
|
||||||
|
return await connection_pool.query("INSERT INTO channels VALUES (?, ?, ?)", [channel_snowflake, server_snowflake, channel_name]).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMessageContentIfPresent(message_snowflake, message_content, message_timestamp) {
|
||||||
|
return connection_pool.query("SELECT message_snowflake FROM messages WHERE message_snowflake = ?", [message_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length == 0) {
|
||||||
|
logger.info("Message specified doesn't exist, probably created before breadbot was here")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return await connection_pool.query(
|
||||||
|
"INSERT INTO message_content_changes (message_snowflake, message_change_old_timestamp, message_change_old_content) " +
|
||||||
|
"SELECT message_snowflake, message_timestamp, message_content FROM messages WHERE message_snowflake = ?;" +
|
||||||
|
"UPDATE messages SET message_timestamp = ?, message_content = ? WHERE message_snowflake = ?;",
|
||||||
|
[message_snowflake, message_timestamp, message_content, message_snowflake]
|
||||||
|
).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function markMessageDeletedIfPresent(message_snowflake) {
|
||||||
|
return connection_pool.query("SELECT message_snowflake FROM messages WHERE message_snowflake = ?", [message_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length == 0) {
|
||||||
|
logger.info("Message specified doesn't exists, probably created before breadbot was here")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return await connection_pool.query(
|
||||||
|
"UPDATE messages SET message_deleted = 1 WHERE message_snowflake = ?", [message_snowflake]
|
||||||
|
).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerAttachmentIfMissing(attachment_snowflake, message_snowflake, attachment_name, attachment_description, attachment_timestamp, attachment_mime_type, attachment_url) {
|
||||||
|
return connection_pool.query("SELECT attachment_snowflake FROM message_attachments WHERE attachment_snowflake = ?", [attachment_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length != 0) {
|
||||||
|
logger.info("Attachment alreaedy exists")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return await connection_pool.query(
|
||||||
|
"INSERT INTO message_attachments (attachment_snowflake, message_snowflake, attachment_name, attachment_description, attachment_timestamp, attachment_mime_type, attachment_url) " +
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
[attachment_snowflake, message_snowflake, attachment_name, attachment_description, attachment_timestamp, attachment_mime_type, attachment_url]
|
||||||
|
).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerUserIfMissing(user_snowflake, user_name, user_displayname) {
|
||||||
|
return connection_pool.query("SELECT * FROM users WHERE user_snowflake = ?;", [user_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length != 0) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return await connection_pool.query("INSERT INTO users VALUES (?, ?, ?)", [user_snowflake, user_name, user_displayname]).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerMessage(message_snowflake, channel_snowflake, user_snowflake, message_content, message_timestamp) {
|
||||||
|
return connection_pool.query("INSERT INTO messages VALUES (?, ?, ?, ?, ?, 0)", [message_snowflake, channel_snowflake, user_snowflake, message_content, message_timestamp]).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function inCall(server_snowflake, channel_snowflake) {
|
||||||
|
return connection_pool.query("SELECT call_id FROM call_states WHERE server_snowflake = ? AND channel_snowflake = ? AND call_end_time IS NULL", [server_snowflake, channel_snowflake]).then(async ([rows, fields]) => {
|
||||||
|
if (rows.length == 0) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return rows[0].call_id
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerNewCall(server_snowflake, channel_snowflake, call_start_time) {
|
||||||
|
return connection_pool.query("INSERT INTO call_states (server_snowflake, channel_snowflake, call_start_time, call_end_time, call_consolidated, call_transcribed, call_data_cleaned_up) VALUES (?, ?, ?, NULL, 0, 0, 0)", [server_snowflake, channel_snowflake, call_start_time]).then(async ([rows, fields]) => {
|
||||||
|
if (typeof rows.insertId === 'undefined') {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return rows.insertId
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerUserInCall(call_id, user_snowflake) {
|
||||||
|
return connection_pool.query("INSERT INTO call_users (call_id, user_snowflake) VALUES (?, ?)", [call_id, user_snowflake]).then(([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deregisterUserInCall(call_id, user_snowflake) {
|
||||||
|
return connection_pool.query("DELETE FROM call_users WHERE call_id = ? AND user_snowflake = ?", [call_id, user_snowflake]).then(([rows, field]) => {
|
||||||
|
return true
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNumberUsersInCall(call_id) {
|
||||||
|
return connection_pool.query("SELECT COUNT(call_users_id) AS users_in_call FROM call_users WHERE call_id = ?", [call_id]).then(([rows, fields]) => {
|
||||||
|
return rows[0].users_in_call
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return -1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCallEndTime(call_id, call_end_time) {
|
||||||
|
return await connection_pool.query("UPDATE call_states SET call_end_time = ? WHERE call_id = ?", [call_end_time, call_id]).then(async ([rows, fields]) => {
|
||||||
|
return true
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error(error)
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildPool,
|
||||||
|
registerMessage,
|
||||||
|
registerServerIfMissing,
|
||||||
|
registerChannelIfMissing,
|
||||||
|
registerUserIfMissing,
|
||||||
|
registerNewCall,
|
||||||
|
updateCallEndTime,
|
||||||
|
inCall,
|
||||||
|
updateMessageContentIfPresent,
|
||||||
|
markMessageDeletedIfPresent,
|
||||||
|
registerAttachmentIfMissing,
|
||||||
|
registerUserInCall,
|
||||||
|
deregisterUserInCall,
|
||||||
|
getNumberUsersInCall
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user