Skip to content

Commit

Permalink
party improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeldaFan0225 committed Aug 5, 2023
1 parent 984a507 commit 6c6fe79
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 27 deletions.
12 changes: 12 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## V3.2.0

- pay for every generation in a party, let the bot create a shared key which everybody will automatically when creating an image in the party!
- create a list of required words for generations in your party

**Migrating**

MAKE SURE THE FOLLOWING STEPS ARE DONE IN ORDER
- run `ALTER TABLE parties ADD COLUMN shared_key VARCHAR(100)` on your postgres database before deploying this or any future version when using a database
- run `ALTER TABLE parties ADD COLUMN wordlist text[] NOT NULL DEFAULT '{}'` on your postgres database


## V3.1.0

- save horde user id for easy lookup
Expand Down
7 changes: 6 additions & 1 deletion config.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ Here you can see an explanation of what which option does
"enabled": Whether the remix action is enabled (BOOLEAN),
"mention_roles": The roles to mention when a party is started (ARRAY OF ROLE IDS),
"default": {
"recurring": The default for recurring awards (BOOLEAN)
"recurring": The default for recurring awards (BOOLEAN),
"pay_for_generations": The default for paying for generations (BOOLEAN)
},
"user_restrictions": {
"award": {
Expand All @@ -209,6 +210,10 @@ Here you can see an explanation of what which option does
"duration": {
"min": (BOOLEAN) *5,
"max": (BOOLEAN) *5,
},
"wordlist": {
"min": (BOOLEAN),
"max": (BOOLEAN),
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zeldafan_discord_bot",
"version": "3.1.0",
"version": "3.2.0",
"description": "",
"main": "dist/index.js",
"scripts": {
Expand All @@ -20,7 +20,7 @@
"homepage": "https://github.com/ZeldaFan0225/AI_Horde_Discord#readme",
"dependencies": {
"@thunder04/supermap": "^3.0.2",
"@zeldafan0225/ai_horde": "^5.0.4",
"@zeldafan0225/ai_horde": "^5.0.5",
"centra": "^2.5.0",
"discord.js": "^14.8.0",
"pg": "^8.8.0",
Expand Down
13 changes: 11 additions & 2 deletions src/classes/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {existsSync, mkdirSync, writeFileSync} from "fs"
import { Pool } from "pg";
import crypto from "crypto"
import Centra from "centra";
import { AIHorde, SharedKeyDetails } from "@zeldafan0225/ai_horde";

export class AIHordeClient extends Client {
commands: Store<StoreTypes.COMMANDS>;
Expand Down Expand Up @@ -148,13 +149,21 @@ export class AIHordeClient extends Client {
return p.rows[0]!
}

async cleanUpParties(database?: Pool) {
async cleanUpParties(ai_horde_manager: AIHorde, database?: Pool) {
const expired_parties = await database?.query("DELETE FROM parties WHERE ends_at <= CURRENT_TIMESTAMP RETURNING *").catch(console.error)
if(!expired_parties?.rowCount) return;
for(let party of expired_parties.rows) {
const channel = await this.channels.fetch(party.channel_id).catch(console.error)
if(!channel?.id || channel?.type !== ChannelType.PublicThread) continue;
await channel?.send({content: `This party ended.\n${party.users?.length} users participated.\nThanks to <@${party.creator_id}> for hosting this party`})
let usagestats: SharedKeyDetails = {}
if(party.shared_key) {
const usertoken = await this.getUserToken(party.creator_id, database)
usagestats = await ai_horde_manager.getSharedKey(party.shared_key, {token: usertoken}).catch(console.error) || {}

await ai_horde_manager.deleteSharedKey(party.shared_key, {token: usertoken}).catch(console.error)
}
await channel?.send({content: `This party ended.\n${party.users?.length} users participated.${usagestats ? `\n${usagestats.utilized} kudos have been spent by <@${party.creator_id}> only for generations in this party` : ""}\nThanks to <@${party.creator_id}> for hosting this party`})

}
}

Expand Down
9 changes: 8 additions & 1 deletion src/commands/advanced_generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,21 @@ export default class extends Command {
prompt = style.prompt.slice().replace("{p}", prompt)
prompt = prompt.replace("{np}", !negative_prompt || prompt.includes("###") ? negative_prompt : `###${negative_prompt}`)

if(party && party.wordlist.length) {
if(ctx.client.config.advanced?.dev) {
console.log(party.wordlist)
}
if(!party.wordlist.every(w => prompt.toLowerCase().includes(w))) return ctx.error({error: "Your prompt does not include all required words"})
}

if(ctx.client.config.advanced?.dev) {
console.log(img?.height)
console.log(img?.width)
console.log(height)
console.log(width)
}

const token = user_token || ctx.client.config.default_token || "0000000000"
const token = party?.shared_key || user_token || ctx.client.config.default_token || "0000000000"
let img_data: Buffer | undefined
if(img) {
let img_data_res = await Centra(img.url, "GET")
Expand Down
12 changes: 11 additions & 1 deletion src/commands/end_party.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SlashCommandBuilder } from "discord.js";
import { Command } from "../classes/command";
import { CommandContext } from "../classes/commandContext";
import { SharedKeyDetails } from "@zeldafan0225/ai_horde";

const command_data = new SlashCommandBuilder()
.setName("end_party")
Expand Down Expand Up @@ -28,9 +29,18 @@ export default class extends Command {
if(!party_data?.rowCount) return ctx.error({error: "Unable to end party"})
ctx.client.cache.delete(`party-${ctx.interaction.channelId}`)

let usagestats: SharedKeyDetails = {}

if(party.shared_key) {
const usertoken = await ctx.client.getUserToken(ctx.interaction.user.id, ctx.database)
usagestats = await ctx.ai_horde_manager.getSharedKey(party.shared_key, {token: usertoken}).catch(console.error) || {}

await ctx.ai_horde_manager.deleteSharedKey(party.shared_key, {token: usertoken}).catch(console.error)
}

await ctx.interaction.reply({content: "Party ended.", ephemeral: true})
ctx.interaction.channel?.send({
content: `The party police showed up and broke down this party.\n${party_data.rows[0].users?.length} users participated.\nThanks to <@${party.creator_id}> for hosting this party`
content: `The party police showed up and broke down this party.\n${party_data.rows[0].users?.length} users participated.${usagestats ? `\n${usagestats.utilized} kudos have been spent by <@${party.creator_id}> only for generations in this party` : ""}\nThanks to <@${party.creator_id}> for hosting this party`
})
}
}
13 changes: 10 additions & 3 deletions src/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ export default class extends Command {

prompt = style.prompt.slice().replace("{p}", prompt)
prompt = prompt.replace("{np}", !negative_prompt || prompt.includes("###") ? negative_prompt : `###${negative_prompt}`)

if(party && party.wordlist.length) {
if(ctx.client.config.advanced?.dev) {
console.log(party.wordlist)
}
if(!party.wordlist.every(w => prompt.toLowerCase().includes(w))) return ctx.error({error: "Your prompt does not include all required words"})
}

if(keep_ratio && img?.width && img?.height) {
const ratio = img?.width/img?.height
Expand All @@ -185,7 +192,7 @@ export default class extends Command {
console.log(width)
}

const token = user_token || ctx.client.config.default_token || "0000000000"
const token = party?.shared_key || user_token || ctx.client.config.default_token || "0000000000"
let img_data: Buffer | undefined
if(img) {
let img_data_res = await Centra(img.url, "GET")
Expand Down Expand Up @@ -234,9 +241,9 @@ export default class extends Command {
const generation_start = await ctx.ai_horde_manager.postAsyncImageGenerate(generation_data, {token})
.catch((e) => {
if(ctx.client.config.advanced?.dev) console.error(e)
return e;
return e.message;
})
if(!generation_start || !generation_start.id) return ctx.error({error: `Unable to start generation: ${generation_start.message}`});
if(!generation_start || !generation_start.id) return ctx.error({error: `Unable to start generation: ${generation_start}`});


if (ctx.client.config.logs?.enabled) {
Expand Down
46 changes: 43 additions & 3 deletions src/commands/party.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ const command_data = new SlashCommandBuilder()
.setName("recurring")
.setDescription("If users get rewarded for each generation or only their first")
)
.addBooleanOption(
new SlashCommandBooleanOption()
.setName("pay_for_generations")
.setDescription("Whether to pay for the generations users make")
)
.addStringOption(
new SlashCommandStringOption()
.setName("wordlist")
.setDescription("Set a comma separated list of words the users prompt has to include")
)
}


Expand All @@ -69,6 +79,8 @@ export default class extends Command {
const award = ctx.interaction.options.getInteger("award", true)
const duration = ctx.interaction.options.getInteger("duration", true)
const recurring = !!(ctx.interaction.options.getBoolean("recurring") ?? ctx.client.config.party?.default?.recurring)
const pay = !!(ctx.interaction.options.getBoolean("pay_for_generations") ?? ctx.client.config.party?.default?.pay_for_generations)
const wordlist = (ctx.interaction.options.getString("wordlist") ?? "").split(",").map(w => w.trim().toLowerCase())
const style_raw = ctx.interaction.options.getString("style") ?? ctx.client.config.generate?.default?.style ?? "raw"
const style = ctx.client.horde_styles[style_raw.toLowerCase()]

Expand All @@ -82,6 +94,13 @@ export default class extends Command {
if(!style) return ctx.error({error: "A valid style is required"})
if(ctx.client.config.generate?.blacklisted_styles?.includes(style_raw.toLowerCase())) return ctx.error({error: "The chosen style is blacklisted"})

if(ctx.client.config.party.user_restrictions?.wordlist) {
if(
ctx.client.config.party.user_restrictions?.wordlist.min || 0 > wordlist.length ||
ctx.client.config.party.user_restrictions?.wordlist.max && ctx.client.config.party.user_restrictions?.wordlist.max < wordlist.length
) return ctx.error({error: `Your wordlist must be between ${ctx.client.config.party.user_restrictions?.wordlist.min || "no"} and ${ctx.client.config.party.user_restrictions?.wordlist.max || "unlimited"} words`})
}

await ctx.interaction.deferReply({ephemeral: true})

const thread = await ctx.interaction.channel.threads.create({
Expand All @@ -90,13 +109,30 @@ export default class extends Command {
}).catch(console.error)
if(!thread?.id) return ctx.error({error: "Unable to start party"})

const party = await ctx.database.query(`INSERT INTO parties (channel_id, guild_id, creator_id, ends_at, style, award, recurring) VALUES ($1, $2, $3, CURRENT_TIMESTAMP + interval '${duration} day', $4, $5, $6) RETURNING *`, [
let shared_key_id: string | null = null

if(pay) {
const token = await ctx.client.getUserToken(ctx.interaction.user.id, ctx.database)

const shared_key = await ctx.ai_horde_manager.putSharedKey({
kudos: 100000,
expiry: duration,
name: `Party ${name}`
}, {token}).catch(console.error)

if(shared_key?.id) shared_key_id = shared_key.id
if(ctx.client.config.advanced?.dev) console.log(shared_key_id)
}

const party = await ctx.database.query(`INSERT INTO parties (channel_id, guild_id, creator_id, ends_at, style, award, recurring, shared_key, wordlist) VALUES ($1, $2, $3, CURRENT_TIMESTAMP + interval '${duration} day', $4, $5, $6, $7, $8) RETURNING *`, [
thread.id,
thread.guildId,
ctx.interaction.user.id,
style_raw.toLowerCase(),
award,
recurring
recurring,
shared_key_id,
wordlist
]).catch(console.error)

if(!party?.rowCount) {
Expand All @@ -105,7 +141,11 @@ export default class extends Command {
}

const start = await thread.send({
content: `<@${ctx.interaction.user.id}> started the party "${name}" with the style "${style_raw}".\nYou will get ${award} kudos for ${recurring ? `every generation` : `your first generation`}.\nThe party ends <t:${Math.round((Date.now() + 1000 * 60 * 60 * 24 * duration)/1000)}:R>\n\n${ctx.client.config.party.mention_roles?.length ? ctx.client.config.party.mention_roles.map(r => `<@&${r}>`).join(" ") : ""}`
content: `<@${ctx.interaction.user.id}> started the party "${name}" with the style "${style_raw}".\nYou will get ${award} kudos for ${recurring ? `every generation` : `your first generation`}.\nThe party ends <t:${Math.round((Date.now() + 1000 * 60 * 60 * 24 * duration)/1000)}:R>${wordlist.length ? `\nThe prompt has to include the words: ${wordlist.join(",")}` : ""}${pay && shared_key_id ? "\nThe party creator will pay for all generations 🥳" : ""}\n\n${ctx.client.config.party.mention_roles?.length ? ctx.client.config.party.mention_roles.map(r => `<@&${r}>`).join(" ") : ""}`,
allowedMentions: {
users: [ctx.interaction.user.id],
roles: ctx.client.config.party.mention_roles
}
}).catch(console.error)

await start?.pin().catch(console.error)
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { handleComponents } from "./handlers/componentHandler";
import { handleModals } from "./handlers/modalHandler";
import { Pool } from "pg"
import { handleAutocomplete } from "./handlers/autocompleteHandler";
import {AIHorde} from "@zeldafan0225/ai_horde";
import { AIHorde } from "@zeldafan0225/ai_horde";
import { handleContexts } from "./handlers/contextHandler";
import {existsSync, mkdirSync} from "fs"
import { handleMessageReact } from "./handlers/messageReact";
Expand Down Expand Up @@ -41,7 +41,7 @@ if(client.config.use_database !== false) {

connection.connect().then(async () => {
await connection!.query("CREATE TABLE IF NOT EXISTS user_tokens (index SERIAL, id VARCHAR(100) PRIMARY KEY, token VARCHAR(100) NOT NULL, horde_id int NOT NULL DEFAULT 0)")
await connection!.query("CREATE TABLE IF NOT EXISTS parties (index SERIAL, channel_id VARCHAR(100) PRIMARY KEY, guild_id VARCHAR(100) NOT NULL, creator_id VARCHAR(100) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, ends_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, style VARCHAR(1000) NOT NULL, award INT NOT NULL DEFAULT 1, recurring BOOLEAN NOT NULL DEFAULT false, users VARCHAR(100)[] NOT NULL DEFAULT '{}')")
await connection!.query("CREATE TABLE IF NOT EXISTS parties (index SERIAL, channel_id VARCHAR(100) PRIMARY KEY, guild_id VARCHAR(100) NOT NULL, creator_id VARCHAR(100) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, ends_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, style VARCHAR(1000) NOT NULL, award INT NOT NULL DEFAULT 1, recurring BOOLEAN NOT NULL DEFAULT false, users VARCHAR(100)[] NOT NULL DEFAULT '{}', shared_key VARCHAR(100), wordlist text[] NOT NULL DEFAULT '{}')")
await connection!.query("CREATE TABLE IF NOT EXISTS pending_kudos (index SERIAL, unique_id VARCHAR(200) PRIMARY KEY, target_id VARCHAR(100) NOT NULL, from_id VARCHAR(100) NOT NULL, amount int NOT NULL, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)")
}).catch(console.error);

Expand Down Expand Up @@ -86,8 +86,8 @@ client.on("ready", async () => {
if(client.config.party?.enabled && !client.config.generate?.enabled) throw new Error("When party is enabled the /generate command also needs to be enabled")

if(client.config.party?.enabled && connection) {
await client.cleanUpParties(connection)
setInterval(async () => await client.cleanUpParties(connection), 1000 * 60 * 5)
await client.cleanUpParties(ai_horde_manager, connection)
setInterval(async () => await client.cleanUpParties(ai_horde_manager, connection), 1000 * 60 * 5)
}
})

Expand Down
9 changes: 8 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ export interface Party {
style: string,
award: number,
recurring: boolean,
users: string[]
shared_key?: string,
users: string[],
wordlist: string[]
}

export interface LORAFetchResponse {
Expand Down Expand Up @@ -391,6 +393,7 @@ export interface Config {
mention_roles?: string[],
default?: {
recurring?: boolean
pay_for_generations?: boolean
},
user_restrictions?: {
award?: {
Expand All @@ -400,6 +403,10 @@ export interface Config {
duration?: {
min?: number,
max?: number
},
wordlist?: {
min?: number,
max?: number
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion template.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@
"1234567890123456789"
],
"default": {
"recurring": false
"recurring": false,
"pay_for_generations": false
},
"user_restrictions": {
"award": {
Expand All @@ -234,6 +235,10 @@
"duration": {
"min": 1,
"max": 30
},
"wordlist": {
"min": 0,
"max": 100
}
}
}
Expand Down

0 comments on commit 6c6fe79

Please sign in to comment.