mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-19 08:41:39 +01:00
add better aniv timeouts, add aniv dodges, fix blank user target bug
This commit is contained in:
10
README.md
10
README.md
@@ -48,6 +48,13 @@ If you've been timed out, you can ghost whisper a message to the chatterbot and
|
|||||||
You can only send one message every 5 minutes.
|
You can only send one message every 5 minutes.
|
||||||
Try to bargain for your release with the chatter that shot you, or just call them names.
|
Try to bargain for your release with the chatter that shot you, or just call them names.
|
||||||
|
|
||||||
|
### Aniv timeouts
|
||||||
|
|
||||||
|
When chatter `a_n_i_v` (a_normal_imyt_viewer) or `a_n_e_e_v` sends a message it's stored in the database.
|
||||||
|
If then someone copies them, they might get timed out.
|
||||||
|
These timeouts and dodges are stored. You can get your stats with the `anivtimeouts` command.
|
||||||
|
The current dodge rate is `1/2`, and the timeout duration range is between `30` and `60` seconds.
|
||||||
|
|
||||||
### Leaderboards
|
### Leaderboards
|
||||||
|
|
||||||
There are 3 types of leaderboards: monthlyKD, alltimeKD and qbucks.
|
There are 3 types of leaderboards: monthlyKD, alltimeKD and qbucks.
|
||||||
@@ -126,6 +133,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
|||||||
`monthlyleaderboard`|Get the K/D leaderboard for this month [(info)](#leaderboards)|anyone|`monthlyleaderboard` `kdleaderboard` `leaderboard`|:white_check_mark:
|
`monthlyleaderboard`|Get the K/D leaderboard for this month [(info)](#leaderboards)|anyone|`monthlyleaderboard` `kdleaderboard` `leaderboard`|:white_check_mark:
|
||||||
`alltimeleaderboard`|Get the K/D leaderboard of all time [(info)](#leaderboards)|anyone|`alltimeleaderboard` `alltimekdleaderboard`|:white_check_mark:
|
`alltimeleaderboard`|Get the K/D leaderboard of all time [(info)](#leaderboards)|anyone|`alltimeleaderboard` `alltimekdleaderboard`|:white_check_mark:
|
||||||
`qbucksleaderboard`|Get the current qbucks leaderboard [(info)](#leaderboards)|anyone|`qbucksleaderboard` `moneyleaderboard` `baltop`|:white_check_mark:
|
`qbucksleaderboard`|Get the current qbucks leaderboard [(info)](#leaderboards)|anyone|`qbucksleaderboard` `moneyleaderboard` `baltop`|:white_check_mark:
|
||||||
|
`anivtimeouts`|Get the amount of timeouts, dodges and dodge percentage from aniv timeouts [(info)](#aniv-timeouts)|anyone|`anivtimeouts` `anivtimeout`|:white_check_mark:
|
||||||
|
|
||||||
### Qweribucks/Item commands
|
### Qweribucks/Item commands
|
||||||
|
|
||||||
@@ -174,8 +182,8 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
|||||||
NAME|COMMAND|FUNCTION|ALIASES|COST
|
NAME|COMMAND|FUNCTION|ALIASES|COST
|
||||||
-|-|-|-|-
|
-|-|-|-|-
|
||||||
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
|
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
|
||||||
Silver Bullet|`silverbullet {target}`|Times targeted user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
|
||||||
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
||||||
|
Silver Bullet|`silverbullet {target}`|Times targeted user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
||||||
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
||||||
|
|
||||||
## Cheers
|
## Cheers
|
||||||
|
|||||||
19
src/commands/anivtimeouts.ts
Normal file
19
src/commands/anivtimeouts.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Command, sendMessage } from "commands";
|
||||||
|
import { getAnivTimeouts } from "db/dbAnivTimeouts";
|
||||||
|
import parseCommandArgs from "lib/parseCommandArgs";
|
||||||
|
import User from "user";
|
||||||
|
|
||||||
|
export default new Command({
|
||||||
|
name: 'anivtimeouts',
|
||||||
|
aliases: ['anivtimeouts', 'anivtimeout'],
|
||||||
|
usertype: 'chatter',
|
||||||
|
execution: async (msg, user) => {
|
||||||
|
const args = parseCommandArgs(msg.messageText);
|
||||||
|
const target = args[0] ? await User.initUsername(args[0].toLowerCase()) : user;
|
||||||
|
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist!`, msg.messageId); return; };
|
||||||
|
const { dodge, dead } = await getAnivTimeouts(target);
|
||||||
|
const percentage = ((dodge / (dead + dodge)) * 100);
|
||||||
|
const message = `Aniv timeouts of ${target.displayName}: Dodge: ${dodge}, Timeout: ${dead}. Dodge percentage: ${isNaN(percentage) ? "0" : percentage.toFixed(1)}%`;
|
||||||
|
await sendMessage(message, msg.messageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -2,12 +2,31 @@ import db from "db/connection";
|
|||||||
import User from "user";
|
import User from "user";
|
||||||
import { anivTimeouts } from "db/schema";
|
import { anivTimeouts } from "db/schema";
|
||||||
import { type anivBots } from "lib/handleAnivMessage";
|
import { type anivBots } from "lib/handleAnivMessage";
|
||||||
|
import { count, eq, and } from "drizzle-orm";
|
||||||
|
|
||||||
|
/** To create a dodge record, set the duration to 0 */
|
||||||
export async function createAnivTimeoutRecord(message: string, anivBot: anivBots, user: User, duration: number) {
|
export async function createAnivTimeoutRecord(message: string, anivBot: anivBots, user: User, duration: number) {
|
||||||
await db.insert(anivTimeouts).values({
|
await db.insert(anivTimeouts).values({
|
||||||
message,
|
message,
|
||||||
anivBot,
|
anivBot,
|
||||||
user: parseInt(user.id),
|
user: parseInt(user.id),
|
||||||
duration
|
duration,
|
||||||
|
timeout: duration !== 0
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function getAnivTimeouts(user: User) {
|
||||||
|
let [dodge, dead] = await Promise.all([
|
||||||
|
db.select({
|
||||||
|
dodge: count()
|
||||||
|
}).from(anivTimeouts).where(and(eq(anivTimeouts.user, parseInt(user.id)), eq(anivTimeouts.timeout, false))).then(a => a[0]?.dodge),
|
||||||
|
db.select({
|
||||||
|
dead: count()
|
||||||
|
}).from(anivTimeouts).where(and(eq(anivTimeouts.user, parseInt(user.id)), eq(anivTimeouts.timeout, true))).then(a => a[0]?.dead)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!dodge) dodge = 0;
|
||||||
|
if (!dead) dead = 0;
|
||||||
|
|
||||||
|
return { dodge, dead };
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AccessToken } from "@twurple/auth";
|
import type { AccessToken } from "@twurple/auth";
|
||||||
import type { inventory, items } from "items";
|
import type { inventory, items } from "items";
|
||||||
import { integer, jsonb, pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
|
import { boolean, integer, jsonb, pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
|
||||||
import type { anivBots } from "lib/handleAnivMessage";
|
import type { anivBots } from "lib/handleAnivMessage";
|
||||||
import { relations } from "drizzle-orm";
|
import { relations } from "drizzle-orm";
|
||||||
|
|
||||||
@@ -95,8 +95,9 @@ export const anivTimeouts = pgTable('anivTimeouts', {
|
|||||||
user: integer().notNull().references(() => users.id),
|
user: integer().notNull().references(() => users.id),
|
||||||
message: varchar().notNull(),
|
message: varchar().notNull(),
|
||||||
anivBot: varchar().$type<anivBots>().notNull(),
|
anivBot: varchar().$type<anivBots>().notNull(),
|
||||||
duration: integer().notNull(),
|
duration: integer(),
|
||||||
created: timestamp().defaultNow().notNull()
|
created: timestamp().defaultNow().notNull(),
|
||||||
|
timeout: boolean().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const anivTimeoutsRelations = relations(anivTimeouts, ({ one }) => ({
|
export const anivTimeoutsRelations = relations(anivTimeouts, ({ one }) => ({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base"
|
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base"
|
||||||
import { streamerId, eventSub, commandPrefix, streamerUsers, chatterId } from "main";
|
import { streamerId, eventSub, commandPrefix, streamerUsers } from "main";
|
||||||
import User from "user";
|
import User from "user";
|
||||||
import commands, { Command, sendMessage, specialAliasCommands } from "commands";
|
import commands, { Command, sendMessage, specialAliasCommands } from "commands";
|
||||||
import { redis } from "bun";
|
import { redis } from "bun";
|
||||||
@@ -30,7 +30,7 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
|
|||||||
|
|
||||||
if (await redis.exists(`user:${user?.id}:bot`)) return; // Ignore all bot commands
|
if (await redis.exists(`user:${user?.id}:bot`)) return; // Ignore all bot commands
|
||||||
|
|
||||||
if (!await redis.exists(`user:${user?.id}:haschatted`) && !msg.sourceMessageId) {
|
if (!await redis.exists(`user:${user?.id}:haschatted`) && !msg.sourceMessageId) { // The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
|
||||||
const message = await sendMessage(`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`);
|
const message = await sendMessage(`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`);
|
||||||
await redis.set(`user:${user?.id}:haschatted`, "1");
|
await redis.set(`user:${user?.id}:haschatted`, "1");
|
||||||
await redis.set(`user:${user?.id}:welcomemessageid`, message.id);
|
await redis.set(`user:${user?.id}:welcomemessageid`, message.id);
|
||||||
@@ -69,8 +69,7 @@ async function handleChatMessage(msg: EventSubChannelChatMessageEvent, user: Use
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await selection.execute(msg, user, { activation });
|
await selection.execute(msg, user, { activation });
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
logger.err(err as string);
|
logger.err(err as string);
|
||||||
await sendMessage('ERROR: Something went wrong', msg.messageId);
|
await sendMessage('ERROR: Something went wrong', msg.messageId);
|
||||||
await user.clearLock();
|
await user.clearLock();
|
||||||
|
|||||||
@@ -42,10 +42,19 @@ export default async function handleMessage(msg: EventSubChannelChatMessageEvent
|
|||||||
await redis.set('anivmessages', JSON.stringify(data));
|
await redis.set('anivmessages', JSON.stringify(data));
|
||||||
} else {
|
} else {
|
||||||
const data = await isAnivMessage(msg.messageText);
|
const data = await isAnivMessage(msg.messageText);
|
||||||
if (data.isAnivMessage) await Promise.all([
|
if (data.isAnivMessage) {
|
||||||
|
if (Math.random() > 0.5) { // 1/2 chance to dodge aniv timeout
|
||||||
|
await createAnivTimeoutRecord(msg.messageText, data.anivbot, user, 0)
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const duration = Math.floor(Math.random() * 30) + 30 // minimum timeout of 30 sec, maximum of 60 sec
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
timeout(user, 'copied an aniv message', 30),
|
timeout(user, 'copied an aniv message', 30),
|
||||||
sendMessage(`${user.displayName} got timed out for copying an ${data.anivbot} message`),
|
sendMessage(`${user.displayName} got timed out for copying an ${data.anivbot} message`),
|
||||||
createAnivTimeoutRecord(msg.messageText, data.anivbot, user, 30)
|
createAnivTimeoutRecord(msg.messageText, data.anivbot, user, duration)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ export default function parseCommandArgs(input: string, specialAlias?: string) {
|
|||||||
let nice = '';
|
let nice = '';
|
||||||
let sliceLength = 0;
|
let sliceLength = 0;
|
||||||
if (specialAlias) {
|
if (specialAlias) {
|
||||||
nice = input.toLowerCase().slice(specialAlias.length).trim();
|
nice = input.toLowerCase().slice(specialAlias.length).replace(/[^\x00-\x7F]/g, '').trim();
|
||||||
} else {
|
} else {
|
||||||
nice = input.toLowerCase().slice(commandPrefix.length).trim();
|
nice = input.toLowerCase().slice(commandPrefix.length).replace(/[^\x00-\x7F]/g, '').trim();
|
||||||
sliceLength = nice.startsWith('use') ? 2 : 1;
|
sliceLength = nice.startsWith('use') ? 2 : 1;
|
||||||
}
|
}
|
||||||
return nice.split(' ').slice(sliceLength).map(a => a.replaceAll(/!/gi, ''));
|
return nice.split(' ').slice(sliceLength).map(a => a.replaceAll(/!/gi, ''));
|
||||||
@@ -17,7 +17,7 @@ export function parseCheerArgs(input: string) {
|
|||||||
const nice = input.toLowerCase().trim();
|
const nice = input.toLowerCase().trim();
|
||||||
|
|
||||||
// This is for the test command. Remove the command prefix, the command, the whitespace after and the amount of fake bits
|
// This is for the test command. Remove the command prefix, the command, the whitespace after and the amount of fake bits
|
||||||
if (nice.startsWith(commandPrefix + 'testcheer')) return nice.slice(commandPrefix.length + 'testcheer'.length + 1).split(' ').slice(1);
|
if (nice.startsWith(commandPrefix + 'testcheer')) return nice.slice(commandPrefix.length + 'testcheer'.length + 1).replaceAll(/\W/g, '').split(' ').slice(1);
|
||||||
|
|
||||||
// This is for actual cheers. Remove all 'cheerx' parts of the message
|
// This is for actual cheers. Remove all 'cheerx' parts of the message
|
||||||
return nice.split(' ').filter(a => !/cheer[0-9]+/i.test(a));
|
return nice.split(' ').filter(a => !/cheer[0-9]+/i.test(a));
|
||||||
|
|||||||
Reference in New Issue
Block a user