mirror of
https://gitlab.com/qwerinope/qweribot.git
synced 2026-02-04 12:36:59 +01:00
Compare commits
13 Commits
576f28df9d
...
v1
| Author | SHA1 | Date | |
|---|---|---|---|
| d23d1a8f7d | |||
| dae3d90397 | |||
| 7843ebaa16 | |||
| 88419e42a2 | |||
| b56f179717 | |||
| a1c93ef64b | |||
| adb0419aaf | |||
| fc8cc82478 | |||
| a2ab327c83 | |||
| a5d3cad00b | |||
| c2fac183cd | |||
| 21c0c5db43 | |||
| d9418a3224 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -139,4 +139,4 @@ dist
|
|||||||
pb/data
|
pb/data
|
||||||
|
|
||||||
# config files
|
# config files
|
||||||
auth.json
|
.env.*
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ ITEM|FUNCTION|ALIASES
|
|||||||
-|-|-
|
-|-|-
|
||||||
`blaster {target}`|Times the target user out for 60 seconds|`!blast, !blaster`
|
`blaster {target}`|Times the target user out for 60 seconds|`!blast, !blaster`
|
||||||
`silverbullet {target}`|Times the target user out for 24 hours|`!execute, !silverbullet`
|
`silverbullet {target}`|Times the target user out for 24 hours|`!execute, !silverbullet`
|
||||||
|
`revive {target}`|Reduce timeout timer of target by 30 seconds|`!revive, !heal`
|
||||||
|
`superrevive {target}`|Reduce timeout timer of target by 12|`!superrevive, !superheal`
|
||||||
`grenade`|Times a random chatter out for 60 seconds|`!grenade`
|
`grenade`|Times a random chatter out for 60 seconds|`!grenade`
|
||||||
`tnt`|Times out 1 to 10 chatters for 60 seconds|`!tnt`
|
`tnt`|Times out 1 to 10 chatters for 60 seconds|`!tnt`
|
||||||
`lootbox`|Gives the user some qbucks, and possibly some items|`!lootbox`
|
`lootbox`|Gives the user some qbucks, and possibly some items|`!lootbox`
|
||||||
@@ -105,7 +107,7 @@ VARIABLE|DEFAULT|FUNCTION|REQUIRED
|
|||||||
`COOLDOWN`|24 Hours|Cooldown between letting users get a lootbox with `!getloot` in seconds|:x:
|
`COOLDOWN`|24 Hours|Cooldown between letting users get a lootbox with `!getloot` in seconds|:x:
|
||||||
`CLIENT_ID`|None|Set the CLIENT_ID to authenticate the bot|:bangbang:
|
`CLIENT_ID`|None|Set the CLIENT_ID to authenticate the bot|:bangbang:
|
||||||
`CLIENT_SECRET`|None|Set the CLIENT_SECRET to authenticate the bot|:bangbang:
|
`CLIENT_SECRET`|None|Set the CLIENT_SECRET to authenticate the bot|:bangbang:
|
||||||
`REDIRECT_URI`|`https://qweri0p.github.io/url-params/`|The REDIRECT_URI set in the twitch dev console|:bangbang:
|
`REDIRECT_URI`|`https://qwerinope.github.io/url-params/`|The REDIRECT_URI set in the twitch dev console|:bangbang:
|
||||||
`OAUTH_CODE`|None|Authorization code for OAuth|:bangbang:
|
`OAUTH_CODE`|None|Authorization code for OAuth|:bangbang:
|
||||||
`DIFFERENT_BROADCASTER`|`false`|Set this to true when `BOT_NAME` and `CHANNEL` are different.|:white_check_mark:
|
`DIFFERENT_BROADCASTER`|`false`|Set this to true when `BOT_NAME` and `CHANNEL` are different.|:white_check_mark:
|
||||||
`BROADCASER_OAUTH_CODE`|None|OAuth authorization code for the broadcaster (ignored if `DIFFERENT_BROADCASTER` is false)|:bangbang:
|
`BROADCASER_OAUTH_CODE`|None|OAuth authorization code for the broadcaster (ignored if `DIFFERENT_BROADCASTER` is false)|:bangbang:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { changeItemCount } from "../lib/items";
|
|||||||
import { changeBalance } from "../lib/userHelper";
|
import { changeBalance } from "../lib/userHelper";
|
||||||
import { vulnerableUsers } from "../lib/timeoutHelper";
|
import { vulnerableUsers } from "../lib/timeoutHelper";
|
||||||
|
|
||||||
const give = createBotCommand('give', async (params, { say, broadcasterId, userId, userName }) => {
|
const give = createBotCommand('give', async (params, { say, broadcasterId, userId }) => {
|
||||||
if (userId !== broadcasterId) return
|
if (userId !== broadcasterId) return
|
||||||
|
|
||||||
const target = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
const target = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
||||||
@@ -19,10 +19,10 @@ const give = createBotCommand('give', async (params, { say, broadcasterId, userI
|
|||||||
else if (data.reason === 'noexist') { await say(`Can't find item ${params[1]}`); return }
|
else if (data.reason === 'noexist') { await say(`Can't find item ${params[1]}`); return }
|
||||||
|
|
||||||
const selection = items.find(item => item.name === params[1].toLowerCase())
|
const selection = items.find(item => item.name === params[1].toLowerCase())
|
||||||
await say(`${target.name} now has ${data.count} ${params[1]}${data.count === 1 ? '' : selection?.plural}`)
|
await say(`${target.displayName} now has ${data.count} ${params[1]}${data.count === 1 ? '' : selection?.plural}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
const vulnChatters = createBotCommand('vulnchatters', async (_params, { say, userId, broadcasterId, userName }) => {
|
const vulnChatters = createBotCommand('vulnchatters', async (_params, { say, userId, broadcasterId }) => {
|
||||||
if (userId !== broadcasterId) return
|
if (userId !== broadcasterId) return
|
||||||
|
|
||||||
await say(`There are ${vulnerableUsers.length} vulnerable chatters`)
|
await say(`There are ${vulnerableUsers.length} vulnerable chatters`)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function getTimeDifference(date1: number, date2: number) {
|
|||||||
|
|
||||||
export default createBotCommand('getloot', async (_params, { reply, userId }) => {
|
export default createBotCommand('getloot', async (_params, { reply, userId }) => {
|
||||||
const user = await api.users.getUserById(userId)
|
const user = await api.users.getUserById(userId)
|
||||||
const data = await lootboxReady(user)
|
const data = await lootboxReady(user!)
|
||||||
if (!data.result) {
|
if (!data.result) {
|
||||||
const { days, hours, minutes, seconds } = getTimeDifference(data.lastlootbox, Date.now() - COOLDOWN)
|
const { days, hours, minutes, seconds } = getTimeDifference(data.lastlootbox, Date.now() - COOLDOWN)
|
||||||
await reply(`lootbox ready in:
|
await reply(`lootbox ready in:
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import getloot from "./getloot";
|
|||||||
import modme from "./modme";
|
import modme from "./modme";
|
||||||
import use from "./use";
|
import use from "./use";
|
||||||
import iteminfo from "./iteminfo"
|
import iteminfo from "./iteminfo"
|
||||||
|
import leaderboard from "./leaderboard"
|
||||||
|
|
||||||
import stats from "./stats"
|
import stats from "./stats"
|
||||||
import aliases from "./itemAliases"
|
import aliases from "./itemAliases"
|
||||||
import admin from "./admin"
|
import admin from "./admin"
|
||||||
|
|
||||||
export default [timeout, inventory, qbucks, getloot, modme, use, iteminfo, ...aliases, ...admin, ...stats]
|
export default [timeout, inventory, qbucks, getloot, modme, use, iteminfo, leaderboard, ...aliases, ...admin, ...stats]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import api from "../lib/api";
|
|||||||
import items from "../items";
|
import items from "../items";
|
||||||
import { HelixUser } from "@twurple/api";
|
import { HelixUser } from "@twurple/api";
|
||||||
|
|
||||||
export default createBotCommand('inv', async (params, { userName, say }) => {
|
export default createBotCommand('inv', async (params, { userName, say, userDisplayName }) => {
|
||||||
let user: HelixUser | null
|
let user: HelixUser | null
|
||||||
if (params.length !== 0) {
|
if (params.length !== 0) {
|
||||||
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
||||||
@@ -24,10 +24,10 @@ export default createBotCommand('inv', async (params, { userName, say }) => {
|
|||||||
messagedata.push(`${itemselection?.prettyname}${amount === 1 ? '' : itemselection?.plural}: ${amount}`)
|
messagedata.push(`${itemselection?.prettyname}${amount === 1 ? '' : itemselection?.plural}: ${amount}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messagedata.length === 0) { await say(`${data.me ? userName : params[0]} has no items!`); return }
|
if (messagedata.length === 0) { await say(`${data.me ? userDisplayName : params[0]} has no items!`); return }
|
||||||
|
|
||||||
await say(`
|
await say(`
|
||||||
inventory of ${data.me ? userName : params[0]}:
|
inventory of ${data.me ? userDisplayName : user.displayName}:
|
||||||
${messagedata.join(', ')}
|
${messagedata.join(', ')}
|
||||||
`)
|
`)
|
||||||
}, { aliases: ['inventory'] })
|
}, { aliases: ['inventory'] })
|
||||||
|
|||||||
@@ -2,28 +2,36 @@ import { BotCommand, createBotCommand } from "@twurple/easy-bot";
|
|||||||
|
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import items from "../items";
|
import items from "../items";
|
||||||
|
import { ITEMBUSY, toggleBusy } from "../lib/items";
|
||||||
|
|
||||||
const aliascommands: BotCommand[] = []
|
const aliascommands: BotCommand[] = []
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
aliascommands.push(createBotCommand(item.name, async (params, { say, broadcasterId, userId }) => {
|
aliascommands.push(createBotCommand(item.name, async (params, { say, reply, broadcasterId, userId }) => {
|
||||||
|
if (ITEMBUSY) { await reply(`There is currently an item in use. Try again.`); return }
|
||||||
const user = await api.users.getUserById(userId)
|
const user = await api.users.getUserById(userId)
|
||||||
|
toggleBusy()
|
||||||
switch (item.name) {
|
switch (item.name) {
|
||||||
case 'blaster':
|
case 'blaster':
|
||||||
case 'silverbullet':
|
case 'silverbullet':
|
||||||
if (params[0] === undefined) { await say('nice miss bro'); return }
|
case 'revive':
|
||||||
|
case 'superrevive':
|
||||||
|
if (params[0] === undefined) { await reply('Please specify a target'); return }
|
||||||
await item.execute(user!, say, broadcasterId, params[0].replace(/[@]/g, ''))
|
await item.execute(user!, say, broadcasterId, params[0].replace(/[@]/g, ''))
|
||||||
break
|
break
|
||||||
case 'grenade':
|
case 'grenade':
|
||||||
case 'tnt':
|
case 'tnt':
|
||||||
|
await item.execute(user!, say, broadcasterId)
|
||||||
|
break
|
||||||
case 'lootbox':
|
case 'lootbox':
|
||||||
await item.execute(user!, say)
|
await item.execute(user!, say)
|
||||||
break
|
break
|
||||||
case 'clipboard':
|
case 'clipboard':
|
||||||
if (params[0] === undefined) { await say("Please specify what the clipboard asks") }
|
if (params[0] === undefined) { await reply("Please specify what the clipboard asks") }
|
||||||
await item.execute(user!, say, broadcasterId, params.join(' '))
|
await item.execute(user!, say, broadcasterId, params.join(' '))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
toggleBusy()
|
||||||
}, { aliases: item.aliases }))
|
}, { aliases: item.aliases }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ export default createBotCommand('iteminfo', async (params, { say }) => {
|
|||||||
if (params[0] === undefined) { await say('No item specified!'); return }
|
if (params[0] === undefined) { await say('No item specified!'); return }
|
||||||
const selection = items.find(item => item.aliases.includes(params[0].toLowerCase()))
|
const selection = items.find(item => item.aliases.includes(params[0].toLowerCase()))
|
||||||
if (!selection) { await say('Item not found'); return }
|
if (!selection) { await say('Item not found'); return }
|
||||||
await say(selection[1].description)
|
await say(selection.description)
|
||||||
}, { aliases: ['item'] })
|
}, { aliases: ['item'] })
|
||||||
|
|||||||
22
src/commands/leaderboard.ts
Normal file
22
src/commands/leaderboard.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { createBotCommand } from "@twurple/easy-bot"
|
||||||
|
import pb, { User } from "../lib/pocketbase"
|
||||||
|
import { getTimeouts } from "../lib/userHelper"
|
||||||
|
|
||||||
|
type KDData = { user: User, KD: number }
|
||||||
|
|
||||||
|
export default createBotCommand('leaderboard', async (_params, { say }) => {
|
||||||
|
const users = await pb.collection('users').getFullList()
|
||||||
|
let userKDs: KDData[] = []
|
||||||
|
for (const user of users) {
|
||||||
|
const data = await getTimeouts(user.id)
|
||||||
|
if (data.hit.blaster < 5) continue
|
||||||
|
const KD = data.shot.blaster / data.hit.blaster
|
||||||
|
const obj: KDData = { user, KD }
|
||||||
|
userKDs.push(obj)
|
||||||
|
}
|
||||||
|
if (userKDs.length === 0) { await say('No users on leaderboard yet!'); return }
|
||||||
|
userKDs.sort((data1, data2) => data2.KD - data1.KD)
|
||||||
|
const textlist: string[] = []
|
||||||
|
for (let i = 0; i < (userKDs.length < 5 ? userKDs.length : 5); i++) textlist.push(`${i + 1}. ${userKDs.at(i)!.user.firstname}: ${userKDs.at(i)!.KD.toFixed(2)}`)
|
||||||
|
await say(`${textlist.join(' | ')}`)
|
||||||
|
}, { aliases: ['kdleaderboard'] })
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { createBotCommand } from "@twurple/easy-bot";
|
import { createBotCommand } from "@twurple/easy-bot";
|
||||||
import api, { broadcasterApi } from "../lib/api";
|
import api, { broadcasterApi } from "../lib/api";
|
||||||
|
import { MODS } from "../lib/timeoutHelper";
|
||||||
const MODS = process.env.MODS
|
|
||||||
if (!MODS) { console.error("Please set the MODS environment variable."); process.exit(1) }
|
|
||||||
|
|
||||||
export default createBotCommand('modme', async (_params, { userName, broadcasterId, userId }) => {
|
export default createBotCommand('modme', async (_params, { userName, broadcasterId, userId }) => {
|
||||||
if (!MODS.includes(userName)) return
|
if (!MODS!.includes(userName)) return
|
||||||
|
|
||||||
if (broadcasterApi) await broadcasterApi.moderation.addModerator(broadcasterId, userId)
|
if (broadcasterApi) await broadcasterApi.moderation.addModerator(broadcasterId, userId)
|
||||||
else await api.moderation.addModerator(broadcasterId, userId)
|
else await api.moderation.addModerator(broadcasterId, userId)
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ export default createBotCommand('balance', async (params, { userName, say }) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await getBalance(user)
|
const data = await getBalance(user)
|
||||||
await say(`${user.name} has ${data.balance} qbucks`)
|
await say(`${user.displayName} has ${data.balance} qbucks`)
|
||||||
|
|
||||||
}, { aliases: ['qbucks', 'qweribucks', 'bal'] })
|
}, { aliases: ['qbucks', 'qweribucks', 'bal'] })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import api from "../lib/api";
|
|||||||
import { getStats } from "../lib/userHelper";
|
import { getStats } from "../lib/userHelper";
|
||||||
import { HelixUser } from "@twurple/api";
|
import { HelixUser } from "@twurple/api";
|
||||||
|
|
||||||
const stats = createBotCommand('stats', async (params, { say, userName }) => {
|
const stats = createBotCommand('stats', async (params, { say, userName, userDisplayName }) => {
|
||||||
let user: HelixUser | null
|
let user: HelixUser | null
|
||||||
if (params.length !== 0) {
|
if (params.length !== 0) {
|
||||||
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
||||||
@@ -21,9 +21,9 @@ const stats = createBotCommand('stats', async (params, { say, userName }) => {
|
|||||||
|
|
||||||
await say(
|
await say(
|
||||||
`
|
`
|
||||||
THIS MONTH: Stats of ${data.me ? userName : params[0]}:
|
THIS MONTH: Stats of ${data.me ? userDisplayName : user.displayName}:
|
||||||
Users blasted: ${data.stats.shot.blaster},
|
Users blasted: ${data.stats.shot.blaster},
|
||||||
Blasted by others: ${data.stats.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(3)} K/D).
|
Blasted by others: ${data.stats.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||||
Grenades lobbed: ${data.stats.used.grenade}
|
Grenades lobbed: ${data.stats.used.grenade}
|
||||||
TNTs lit: ${data.stats.used.tnt},
|
TNTs lit: ${data.stats.used.tnt},
|
||||||
Silver bullets fired: ${data.stats.shot.silverbullet},
|
Silver bullets fired: ${data.stats.shot.silverbullet},
|
||||||
@@ -32,7 +32,7 @@ const stats = createBotCommand('stats', async (params, { say, userName }) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const alltime = createBotCommand('alltime', async (params, { say, userName }) => {
|
const alltime = createBotCommand('alltime', async (params, { say, userName, userDisplayName }) => {
|
||||||
let user: HelixUser | null
|
let user: HelixUser | null
|
||||||
if (params.length !== 0) {
|
if (params.length !== 0) {
|
||||||
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
user = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
||||||
@@ -48,9 +48,9 @@ const alltime = createBotCommand('alltime', async (params, { say, userName }) =>
|
|||||||
|
|
||||||
await say(
|
await say(
|
||||||
`
|
`
|
||||||
ALLTIME: Stats of ${data.me ? userName : params[0]}:
|
ALLTIME: Stats of ${data.me ? userDisplayName : user.displayName}:
|
||||||
Users blasted: ${data.stats.shot.blaster},
|
Users blasted: ${data.stats.shot.blaster},
|
||||||
Blasted by others: ${data.stats.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(3)} K/D).
|
Blasted by others: ${data.stats.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||||
Grenades lobbed: ${data.stats.used.grenade}
|
Grenades lobbed: ${data.stats.used.grenade}
|
||||||
TNTs lit: ${data.stats.used.tnt},
|
TNTs lit: ${data.stats.used.tnt},
|
||||||
Silver bullets fired: ${data.stats.shot.silverbullet},
|
Silver bullets fired: ${data.stats.shot.silverbullet},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { addTimeoutToDB, timeout } from "../lib/timeoutHelper";
|
|||||||
import { changeBalance, getBalance } from "../lib/userHelper";
|
import { changeBalance, getBalance } from "../lib/userHelper";
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
|
|
||||||
export default createBotCommand('timeout', async (params, { say, broadcasterId, userName }) => {
|
export default createBotCommand('timeout', async (params, { say, broadcasterId, userName, userDisplayName }) => {
|
||||||
const attacker = await api.users.getUserByName(userName)
|
const attacker = await api.users.getUserByName(userName)
|
||||||
const userbal = await getBalance(attacker!)
|
const userbal = await getBalance(attacker!)
|
||||||
if (userbal.balance < 100) { await say('not enough qbucks'); return }
|
if (userbal.balance < 100) { await say('not enough qbucks'); return }
|
||||||
@@ -11,7 +11,7 @@ export default createBotCommand('timeout', async (params, { say, broadcasterId,
|
|||||||
const target = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
const target = await api.users.getUserByName(params[0].replace(/[@]/g, ''))
|
||||||
const status = await timeout(broadcasterId, target!, 60, `You got blasted by ${userName}`)
|
const status = await timeout(broadcasterId, target!, 60, `You got blasted by ${userName}`)
|
||||||
if (status.status) {
|
if (status.status) {
|
||||||
await say(`${params[0]} got blasted by ${userName}! ${userName} now has ${userbal.balance - 100} qbucks remaining`)
|
await say(`${target?.displayName} got blasted by ${userDisplayName}! ${userDisplayName} now has ${userbal.balance - 100} qbucks remaining`)
|
||||||
await changeBalance(attacker!, -100)
|
await changeBalance(attacker!, -100)
|
||||||
await addTimeoutToDB(attacker!, target!, 'blaster')
|
await addTimeoutToDB(attacker!, target!, 'blaster')
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ export default createBotCommand('timeout', async (params, { say, broadcasterId,
|
|||||||
await say(`${params[0]} doesn't exist!`)
|
await say(`${params[0]} doesn't exist!`)
|
||||||
break
|
break
|
||||||
case 'banned':
|
case 'banned':
|
||||||
await say(`${params[0]} is already dead!`)
|
await say(`${target?.displayName} is already dead!`)
|
||||||
break
|
break
|
||||||
case 'unknown':
|
case 'unknown':
|
||||||
await say(`NO!`)
|
await say(`NO!`)
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
import { createBotCommand } from "@twurple/easy-bot";
|
import { createBotCommand } from "@twurple/easy-bot";
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import items from "../items";
|
import items from "../items";
|
||||||
|
import { ITEMBUSY, toggleBusy } from "../lib/items";
|
||||||
|
|
||||||
export default createBotCommand('use', async (params, { say, broadcasterId, userId }) => {
|
export default createBotCommand('use', async (params, { say, reply, broadcasterId, userId }) => {
|
||||||
const user = await api.users.getUserById(userId)
|
const user = await api.users.getUserById(userId)
|
||||||
|
|
||||||
if (params[0] === undefined) return
|
if (params[0] === undefined) return
|
||||||
const selection = items.find(item => item.aliases.includes(params[0].toLowerCase()))
|
const selection = items.find(item => item.aliases.includes(params[0].toLowerCase()))
|
||||||
|
|
||||||
if (!selection) { say(`${params[0]} does not exist!`); return }
|
if (!selection) { reply(`${params[0]} does not exist!`); return }
|
||||||
|
|
||||||
|
if (ITEMBUSY) { await reply(`There is currently an item in use. Try again.`); return }
|
||||||
|
|
||||||
|
toggleBusy()
|
||||||
switch (selection.name) {
|
switch (selection.name) {
|
||||||
case 'blaster':
|
case 'blaster':
|
||||||
case 'silverbullet':
|
case 'silverbullet':
|
||||||
if (params[1] === undefined) { await say('nice miss bro'); return }
|
case 'revive':
|
||||||
|
case 'superrevive':
|
||||||
|
if (params[1] === undefined) { await reply('Please specify a target'); return }
|
||||||
await selection.execute(user!, say, broadcasterId, params[1].replace(/[@]/g, ''))
|
await selection.execute(user!, say, broadcasterId, params[1].replace(/[@]/g, ''))
|
||||||
break
|
break
|
||||||
case 'grenade':
|
case 'grenade':
|
||||||
@@ -24,8 +30,9 @@ export default createBotCommand('use', async (params, { say, broadcasterId, user
|
|||||||
await selection.execute(user!, say)
|
await selection.execute(user!, say)
|
||||||
break
|
break
|
||||||
case 'clipboard':
|
case 'clipboard':
|
||||||
if (params[1] === undefined) { await say("Please specify what the clipboard asks")}
|
if (params[1] === undefined) { await reply("Please specify what the clipboard asks") }
|
||||||
await selection.execute(user!, say, broadcasterId, params.slice(1).join(' '))
|
await selection.execute(user!, say, broadcasterId, params.slice(1).join(' '))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
toggleBusy()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export const blaster = {
|
|||||||
|
|
||||||
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no blasters!'); return }
|
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no blasters!'); return }
|
||||||
|
|
||||||
const result = await timeout(broadcasterId, target!, 60, `You got blasted by ${user.name}`)
|
const result = await timeout(broadcasterId, target, 60, `You got blasted by ${user.displayName}`)
|
||||||
if (result.status) {
|
if (result.status) {
|
||||||
await say(`${targetname} got blasted by ${user.name}! ${user.name} has ${itemResult.count} blaster${itemResult.count === 1 ? '' : 's'} remaining`)
|
await say(`${target?.displayName} got blasted by ${user.displayName}! ${user.displayName} has ${itemResult.count} blaster${itemResult.count === 1 ? '' : 's'} remaining`)
|
||||||
await addTimeoutToDB(user, target!, 'blaster')
|
await addTimeoutToDB(user, target!, 'blaster')
|
||||||
await addUsedItem(user, 'blaster')
|
await addUsedItem(user, 'blaster')
|
||||||
await updateInventory(user, itemResult.inv!)
|
await updateInventory(user, itemResult.inv!)
|
||||||
@@ -29,7 +29,7 @@ export const blaster = {
|
|||||||
await say(`${targetname} doesn't exist!`)
|
await say(`${targetname} doesn't exist!`)
|
||||||
break
|
break
|
||||||
case 'banned':
|
case 'banned':
|
||||||
await say(`${targetname} is already dead!`)
|
await say(`${target?.displayName} is already dead!`)
|
||||||
break
|
break
|
||||||
case 'unknown':
|
case 'unknown':
|
||||||
await say(`NO!`)
|
await say(`NO!`)
|
||||||
@@ -52,7 +52,7 @@ export const silverbullet = {
|
|||||||
const itemResult = await changeItemCount(user, 'silverbullet')
|
const itemResult = await changeItemCount(user, 'silverbullet')
|
||||||
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no silver bullets!'); return }
|
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no silver bullets!'); return }
|
||||||
|
|
||||||
const result = await timeout(broadcasterId, target!, 60 * 60 * 24, `You got hit by a silver bullet fired by ${user.name}`)
|
const result = await timeout(broadcasterId, target, 60 * 60 * 24, `You got hit by a silver bullet fired by ${user.displayName}`)
|
||||||
if (result.status) {
|
if (result.status) {
|
||||||
await say(`${target?.name} got deleted.`)
|
await say(`${target?.name} got deleted.`)
|
||||||
await addTimeoutToDB(user, target!, 'silverbullet')
|
await addTimeoutToDB(user, target!, 'silverbullet')
|
||||||
@@ -64,7 +64,7 @@ export const silverbullet = {
|
|||||||
await say(`${targetname} doesn't exist!`)
|
await say(`${targetname} doesn't exist!`)
|
||||||
break
|
break
|
||||||
case 'banned':
|
case 'banned':
|
||||||
await say(`${targetname} is already dead!`)
|
await say(`${target?.displayName} is already dead!`)
|
||||||
break
|
break
|
||||||
case 'unknown':
|
case 'unknown':
|
||||||
await say(`NO!`)
|
await say(`NO!`)
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ export const clipboard = {
|
|||||||
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no clipboards!'); return }
|
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no clipboards!'); return }
|
||||||
|
|
||||||
await tempapi.polls.createPoll(broadcasterId, { choices: ['Yes', 'No'], duration: 120, title: question })
|
await tempapi.polls.createPoll(broadcasterId, { choices: ['Yes', 'No'], duration: 120, title: question })
|
||||||
await say(`${user.name} used a clipboard! They have ${itemResult.count} clipboard${itemResult.count === 1 ? '' : 's'} remaining`)
|
await say(`${user.displayName} used a clipboard! They have ${itemResult.count} clipboard${itemResult.count === 1 ? '' : 's'} remaining`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ export const grenade = {
|
|||||||
|
|
||||||
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no grenades!'); return }
|
if (!itemResult.result && itemResult.reason === 'negative') { await say('You have no grenades!'); return }
|
||||||
const target = await api.users.getUserById(vulnerableUsers[Math.floor(Math.random() * vulnerableUsers.length)])
|
const target = await api.users.getUserById(vulnerableUsers[Math.floor(Math.random() * vulnerableUsers.length)])
|
||||||
const result = await timeout(broadcasterId, target!, 60, `You got hit by ${user.name}'s grenade`)
|
const result = await timeout(broadcasterId, target!, 60, `You got hit by ${user.displayName}'s grenade`)
|
||||||
if (result.status) {
|
if (result.status) {
|
||||||
await say(`${target?.name} got blown up by ${user.name}'s grenade!`)
|
await say(`${target?.displayName} got blown up by ${user.displayName}'s grenade!`)
|
||||||
await addTimeoutToDB(user, target!, 'grenade')
|
await addTimeoutToDB(user, target!, 'grenade')
|
||||||
await addUsedItem(user, 'grenade')
|
await addUsedItem(user, 'grenade')
|
||||||
await updateInventory(user, itemResult.inv!)
|
await updateInventory(user, itemResult.inv!)
|
||||||
@@ -59,9 +59,9 @@ export const tnt = {
|
|||||||
const soontobedeadusers = shuffle(vulnerableUsers).slice(vulnerableUsers.length - blastedusers)
|
const soontobedeadusers = shuffle(vulnerableUsers).slice(vulnerableUsers.length - blastedusers)
|
||||||
const targets = await api.users.getUsersByIds(soontobedeadusers)
|
const targets = await api.users.getUsersByIds(soontobedeadusers)
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
const result = await timeout(broadcasterId, target!, 60, `You got hit by ${user.name}'s TNT`)
|
const result = await timeout(broadcasterId, target!, 60, `You got hit by ${user.displayName}'s TNT`)
|
||||||
if (result.status) {
|
if (result.status) {
|
||||||
await say(`${target?.name} got blown up by TNT!`)
|
await say(`${target?.displayName} got blown up by TNT!`)
|
||||||
await addTimeoutToDB(user, target!, 'tnt')
|
await addTimeoutToDB(user, target!, 'tnt')
|
||||||
await updateInventory(user, itemResult.inv!)
|
await updateInventory(user, itemResult.inv!)
|
||||||
} else {
|
} else {
|
||||||
@@ -71,6 +71,6 @@ export const tnt = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await addUsedItem(user, 'tnt')
|
await addUsedItem(user, 'tnt')
|
||||||
await say(`${user.name} blew up ${blastedusers} chatters with their TNT! ${user.name} has ${itemResult.count} tnt${itemResult.count === 1 ? '' : 's'} remaining`)
|
await say(`${user.displayName} blew up ${blastedusers} chatters with their TNT! ${user.displayName} has ${itemResult.count} tnt${itemResult.count === 1 ? '' : 's'} remaining`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { HelixUser } from "@twurple/api"
|
|||||||
|
|
||||||
import { blaster, silverbullet } from "./blasters"
|
import { blaster, silverbullet } from "./blasters"
|
||||||
import { grenade, tnt } from "./explosives"
|
import { grenade, tnt } from "./explosives"
|
||||||
|
import { revive, superrevive } from "./revives"
|
||||||
import { lootbox } from "./lootbox"
|
import { lootbox } from "./lootbox"
|
||||||
import { clipboard } from "./clipboard"
|
import { clipboard } from "./clipboard"
|
||||||
|
|
||||||
@@ -13,6 +14,6 @@ interface item {
|
|||||||
description: string,
|
description: string,
|
||||||
execute: (user: HelixUser, say: (args0: string) => Promise<void>, broadcasterId?: string, targetname?: string) => Promise<void>
|
execute: (user: HelixUser, say: (args0: string) => Promise<void>, broadcasterId?: string, targetname?: string) => Promise<void>
|
||||||
}
|
}
|
||||||
const data = [blaster, silverbullet, grenade, tnt, lootbox, clipboard] as item[]
|
const data = [blaster, silverbullet, grenade, tnt, revive, superrevive, lootbox, clipboard] as item[]
|
||||||
export const ITEMS = data.map(item => item.name)
|
export const ITEMS = data.map(item => item.name)
|
||||||
export default data
|
export default data
|
||||||
|
|||||||
@@ -27,6 +27,6 @@ export const lootbox = {
|
|||||||
await updateInventory(user, inventory)
|
await updateInventory(user, inventory)
|
||||||
await addUsedItem(user, 'lootbox')
|
await addUsedItem(user, 'lootbox')
|
||||||
|
|
||||||
await say(`${user.name} got: ${newitems.join(' and ')}`)
|
await say(`${user.displayName} got: ${newitems.join(' and ')}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/items/revives.ts
Normal file
73
src/items/revives.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { HelixUser } from "@twurple/api"
|
||||||
|
import api from "../lib/api"
|
||||||
|
import { changeItemCount } from "../lib/items"
|
||||||
|
import { reviveTarget } from "../lib/timeoutHelper"
|
||||||
|
import { addUsedItem, updateInventory } from "../lib/userHelper"
|
||||||
|
|
||||||
|
export const revive = {
|
||||||
|
name: 'revive',
|
||||||
|
prettyname: 'Revive',
|
||||||
|
aliases: ['revive', 'heal'],
|
||||||
|
plural: 's',
|
||||||
|
description: "Use: revive {target}, Function: Reduce timeout timer of target by 30 seconds. Aliases: !revive, !heal",
|
||||||
|
execute: async (user: HelixUser, say: (arg0: string) => Promise<void>, broadcasterId: string, targetname: string) => {
|
||||||
|
const target = await api.users.getUserByName(targetname)
|
||||||
|
|
||||||
|
const itemResult = await changeItemCount(user, 'revive')
|
||||||
|
if (!itemResult.result) { await say('You have no revives!'); return }
|
||||||
|
|
||||||
|
const reviveResult = await reviveTarget(broadcasterId, target, 30)
|
||||||
|
if (reviveResult.status) { await updateInventory(user, itemResult.inv!); await addUsedItem(target!, 'revive') }
|
||||||
|
switch (reviveResult.reason) {
|
||||||
|
case 'noexist':
|
||||||
|
await say(`${targetname} does not exist`)
|
||||||
|
break
|
||||||
|
case 'notbanned':
|
||||||
|
await say(`${target?.displayName} doesn't need revives`)
|
||||||
|
break
|
||||||
|
case 'unknown':
|
||||||
|
await say("Something went wrong!")
|
||||||
|
break
|
||||||
|
case 'healed':
|
||||||
|
await say(`${target?.displayName} got healed for 30 seconds by ${user.displayName}`)
|
||||||
|
break
|
||||||
|
case 'revived':
|
||||||
|
await say(`${target?.displayName} got revived by ${user.displayName}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const superrevive = {
|
||||||
|
name: 'superrevive',
|
||||||
|
prettyname: 'Super Revive',
|
||||||
|
aliases: ['superrevive', 'superheal'],
|
||||||
|
plural: 's',
|
||||||
|
description: "Use: superrevive {target}, Function: Reduce timeout timer of target by 12 hours. Aliases: !superrevive, !superheal",
|
||||||
|
execute: async (user: HelixUser, say: (arg0: string) => Promise<void>, broadcasterId: string, targetname: string) => {
|
||||||
|
const target = await api.users.getUserByName(targetname)
|
||||||
|
|
||||||
|
const itemResult = await changeItemCount(user, 'superrevive')
|
||||||
|
if (!itemResult.result) { await say('You have no revives!'); return }
|
||||||
|
|
||||||
|
const reviveResult = await reviveTarget(broadcasterId, target, 60 * 60 * 12)
|
||||||
|
if (reviveResult.status) { await updateInventory(user, itemResult.inv!); await addUsedItem(target!, 'superrevive') }
|
||||||
|
switch (reviveResult.reason) {
|
||||||
|
case 'noexist':
|
||||||
|
await say(`${targetname} does not exist`)
|
||||||
|
break
|
||||||
|
case 'notbanned':
|
||||||
|
await say(`${target?.displayName} doesn't need revives`)
|
||||||
|
break
|
||||||
|
case 'unknown':
|
||||||
|
await say("Something went wrong!")
|
||||||
|
break
|
||||||
|
case 'healed':
|
||||||
|
await say(`${target?.displayName} got healed for 12 hours by ${user.displayName}`)
|
||||||
|
break
|
||||||
|
case 'revived':
|
||||||
|
await say(`${target?.displayName} got revived by ${user.displayName}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ import { RefreshingAuthProvider, exchangeCode } from '@twurple/auth'
|
|||||||
import pb from './pocketbase'
|
import pb from './pocketbase'
|
||||||
import { RecordModel } from 'pocketbase'
|
import { RecordModel } from 'pocketbase'
|
||||||
|
|
||||||
|
const INTENTS = ['channel:manage:moderators', 'moderation:read', 'moderator:manage:banned_users', 'channel:manage:polls', 'channel:read:polls']
|
||||||
|
|
||||||
let ttvauth: RecordModel | undefined
|
let ttvauth: RecordModel | undefined
|
||||||
try {
|
try {
|
||||||
ttvauth = await pb.collection('ttvauth').getFirstListItem('main=true')
|
ttvauth = await pb.collection('ttvauth').getFirstListItem('main=true')
|
||||||
@@ -28,17 +30,17 @@ async function firstAccess(main = true) {
|
|||||||
const CLIENT_SECRET = process.env.CLIENT_SECRET
|
const CLIENT_SECRET = process.env.CLIENT_SECRET
|
||||||
const OAUTH_CODE = process.env.OAUTH_CODE
|
const OAUTH_CODE = process.env.OAUTH_CODE
|
||||||
const BROADCASTER_OAUTH_CODE = process.env.BROADCASTER_OAUTH_CODE
|
const BROADCASTER_OAUTH_CODE = process.env.BROADCASTER_OAUTH_CODE
|
||||||
const REDIRECT_URI = process.env.REDIRECT_URI ?? 'https://qweri0p.github.io/url-params/'
|
const REDIRECT_URI = process.env.REDIRECT_URI ?? 'https://qwerinope.github.io/url-params/'
|
||||||
|
|
||||||
if (!CLIENT_ID) { console.error("No 'CLIENT_ID' for OAuth defined in environment variables."); process.exit(1) }
|
if (!CLIENT_ID) { console.error("No 'CLIENT_ID' for OAuth defined in environment variables."); process.exit(1) }
|
||||||
if (!CLIENT_SECRET) { console.error("No 'CLIENT_SECRET' for OAuth defined in environment variables."); process.exit(1) }
|
if (!CLIENT_SECRET) { console.error("No 'CLIENT_SECRET' for OAuth defined in environment variables."); process.exit(1) }
|
||||||
if ((main && !OAUTH_CODE) || (!main && !BROADCASTER_OAUTH_CODE)) {
|
if ((main && !OAUTH_CODE) || (!main && !BROADCASTER_OAUTH_CODE)) {
|
||||||
if (main) {
|
if (main) {
|
||||||
console.error("No 'OAUTH_CODE' provided. To get the code, please visit this URL, authorize the bot and copy the 'code' from the return URL.")
|
console.error("No 'OAUTH_CODE' provided. To get the code, please visit this URL, authorize the bot and copy the 'code' from the return URL.")
|
||||||
console.error(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=chat:read+chat:edit+moderator:manage:banned_users+moderation:read+channel:manage:polls+channel:read:polls`)
|
console.error(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=chat:read+chat:edit+${INTENTS.join('+')}`)
|
||||||
} else {
|
} else {
|
||||||
console.error("No 'BROADCASTER_OAUTH_CODE' provided. To get the code, please make the broadcaster visit the following URL, and get them to return the 'code' from the return URL.")
|
console.error("No 'BROADCASTER_OAUTH_CODE' provided. To get the code, please make the broadcaster visit the following URL, and get them to return the 'code' from the return URL.")
|
||||||
console.error(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=moderator:manage:banned_users+moderation:read+channel:manage:moderators+channel:manage:polls+channel:read:polls`)
|
console.error(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=${INTENTS.join('+')}`)
|
||||||
}
|
}
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
@@ -69,7 +71,7 @@ await authProvider.addUserForToken({
|
|||||||
refreshToken: auth.REFRESH_TOKEN,
|
refreshToken: auth.REFRESH_TOKEN,
|
||||||
expiresIn: auth.EXPIRESIN,
|
expiresIn: auth.EXPIRESIN,
|
||||||
obtainmentTimestamp: auth.OBTAINMENTTIMESTAMP
|
obtainmentTimestamp: auth.OBTAINMENTTIMESTAMP
|
||||||
}, ['chat', 'moderator:manage:banned_users', 'channel:manage:polls', 'channel:read:polls', 'channel:manage:moderators'])
|
}, ['chat', ...INTENTS])
|
||||||
|
|
||||||
authProvider.onRefresh(async (_id, newTokenData) => {
|
authProvider.onRefresh(async (_id, newTokenData) => {
|
||||||
auth.ACCESS_TOKEN = newTokenData.accessToken
|
auth.ACCESS_TOKEN = newTokenData.accessToken
|
||||||
@@ -95,7 +97,7 @@ const broadcasterAuthProvider = broadcasterAuthData === undefined ? undefined :
|
|||||||
refreshToken: broadcasterAuthData.REFRESH_TOKEN,
|
refreshToken: broadcasterAuthData.REFRESH_TOKEN,
|
||||||
expiresIn: broadcasterAuthData.EXPIRESIN,
|
expiresIn: broadcasterAuthData.EXPIRESIN,
|
||||||
obtainmentTimestamp: broadcasterAuthData.OBTAINMENTTIMESTAMP
|
obtainmentTimestamp: broadcasterAuthData.OBTAINMENTTIMESTAMP
|
||||||
}, ['moderator:manage:banned_users', 'moderation:read', 'channel:manage:moderators', 'channel:manage:polls', 'channel:read:polls'])
|
}, INTENTS)
|
||||||
|
|
||||||
broadcasterAuthProvider.onRefresh(async (_id, newTokenData) => {
|
broadcasterAuthProvider.onRefresh(async (_id, newTokenData) => {
|
||||||
broadcasterAuthData.ACCESS_TOKEN = newTokenData.accessToken
|
broadcasterAuthData.ACCESS_TOKEN = newTokenData.accessToken
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ interface itemChangeResult {
|
|||||||
count: number,
|
count: number,
|
||||||
inv?: inventory
|
inv?: inventory
|
||||||
}
|
}
|
||||||
|
/** Check if the target user can use/lose item(s) and return the new inventory
|
||||||
|
* @param [amount=-1] If not specified, reduce count by one
|
||||||
|
* @param [preconfirmed=false] If it is confirmed that the change is allowed, update the inventory immediately */
|
||||||
export async function changeItemCount(user: HelixUser, item: string, amount = -1, preconfirmed = false): Promise<itemChangeResult> {
|
export async function changeItemCount(user: HelixUser, item: string, amount = -1, preconfirmed = false): Promise<itemChangeResult> {
|
||||||
if (!ITEMS.includes(item)) return { result: false, reason: 'noexist', count: 0 }
|
if (!ITEMS.includes(item)) return { result: false, reason: 'noexist', count: 0 }
|
||||||
let inv = await getInventory(user)
|
let inv = await getInventory(user)
|
||||||
@@ -24,3 +26,9 @@ export async function changeItemCount(user: HelixUser, item: string, amount = -1
|
|||||||
|
|
||||||
return { result: true, reason: '', count: inv[item], inv }
|
return { result: true, reason: '', count: inv[item], inv }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let ITEMBUSY = false
|
||||||
|
|
||||||
|
export function toggleBusy() {
|
||||||
|
ITEMBUSY = !ITEMBUSY
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ interface lootboxReadyResult {
|
|||||||
lastlootbox: number,
|
lastlootbox: number,
|
||||||
DBuser: User
|
DBuser: User
|
||||||
}
|
}
|
||||||
|
/** Check if the lootbox is ready for specified user */
|
||||||
export async function lootboxReady(user: HelixUser | null): Promise<lootboxReadyResult> {
|
export async function lootboxReady(user: HelixUser): Promise<lootboxReadyResult> {
|
||||||
const DBuser = await pb.collection('users').getFirstListItem(`id="${user!.id}"`)
|
const DBuser = await pb.collection('users').getFirstListItem(`id="${user!.id}"`)
|
||||||
if ((Date.parse(DBuser.lastlootbox) + COOLDOWN) > Date.now()) return { result: false, lastlootbox: Date.parse(DBuser.lastlootbox), DBuser }
|
if ((Date.parse(DBuser.lastlootbox) + COOLDOWN) > Date.now()) return { result: false, lastlootbox: Date.parse(DBuser.lastlootbox), DBuser }
|
||||||
return { result: true, lastlootbox: 0, DBuser }
|
return { result: true, lastlootbox: 0, DBuser }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the time for last time user got lootbox to now */
|
||||||
export async function resetLootboxTimer(user: User) {
|
export async function resetLootboxTimer(user: User) {
|
||||||
const data = { lastlootbox: new Date(Date.now()).toISOString() }
|
const data = { lastlootbox: new Date(Date.now()).toISOString() }
|
||||||
await pb.collection('users').update(user.id, data)
|
await pb.collection('users').update(user.id, data)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import api, { broadcasterApi } from "./api";
|
|||||||
import pb from "./pocketbase";
|
import pb from "./pocketbase";
|
||||||
import { DBValidation } from "./userHelper";
|
import { DBValidation } from "./userHelper";
|
||||||
|
|
||||||
|
const MODSSTRING = process.env.MODS
|
||||||
|
if (!MODSSTRING) { console.error("Please set the MODS environment variable."); process.exit(1) }
|
||||||
|
export const MODS = MODSSTRING.split(',')
|
||||||
|
|
||||||
type shooter = 'blaster' | 'grenade' | 'silverbullet' | 'tnt'
|
type shooter = 'blaster' | 'grenade' | 'silverbullet' | 'tnt'
|
||||||
|
|
||||||
interface statusmessage {
|
interface statusmessage {
|
||||||
@@ -10,18 +14,21 @@ interface statusmessage {
|
|||||||
reason: string
|
reason: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function timeout(broadcasterid: string, target: HelixUser, duration: number, reason: string): Promise<statusmessage> {
|
/** Ban a specific user out by another user for specified amout of time, with specific reason
|
||||||
|
* If the user does not exist or is already banned return status: false
|
||||||
|
* If the user is a moderator, make sure they get their status back after timeout has elapsed */
|
||||||
|
export async function timeout(broadcasterid: string, target: HelixUser | null, duration: number, reason: string): Promise<statusmessage> {
|
||||||
if (!target) return { status: false, reason: 'noexist' }
|
if (!target) return { status: false, reason: 'noexist' }
|
||||||
const tmpapi = broadcasterApi ?? api
|
const tmpapi = broadcasterApi ?? api
|
||||||
if (target.name === process.env.BOT_NAME) return { status: false, reason: 'unknown' }
|
if (target.name === process.env.BOT_NAME) return { status: false, reason: 'unknown' }
|
||||||
if (await tmpapi.moderation.checkUserBan(broadcasterid, target)) return { status: false, reason: 'banned' }
|
if (await tmpapi.moderation.checkUserBan(broadcasterid, target.id)) return { status: false, reason: 'banned' }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (await tmpapi.moderation.checkUserMod(broadcasterid, target)) {
|
if (await tmpapi.moderation.checkUserMod(broadcasterid, target.id)) {
|
||||||
await tmpapi.moderation.removeModerator(broadcasterid, target)
|
await tmpapi.moderation.removeModerator(broadcasterid, target.id)
|
||||||
remodMod(broadcasterid, target, duration * 1000, tmpapi)
|
remodMod(broadcasterid, target, duration * 1000, tmpapi)
|
||||||
}
|
}
|
||||||
await tmpapi.moderation.banUser(broadcasterid, { duration, reason, user: target })
|
await tmpapi.moderation.banUser(broadcasterid, { duration, reason, user: target.id })
|
||||||
await DBValidation(target)
|
await DBValidation(target)
|
||||||
return { status: true, reason: '' }
|
return { status: true, reason: '' }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -30,6 +37,30 @@ export async function timeout(broadcasterid: string, target: HelixUser, duration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Revive a specific target for a certain amount of time */
|
||||||
|
export async function reviveTarget(broadcasterId: string, target: HelixUser | null, duration: number): Promise<statusmessage> {
|
||||||
|
if (!target) return { status: false, reason: 'noexist' }
|
||||||
|
const tmpapi = broadcasterApi ?? api
|
||||||
|
const bandata = await tmpapi.moderation.getBannedUsers(broadcasterId, { userId: target.id })
|
||||||
|
if (!bandata.data[0]) return { status: false, reason: 'notbanned' }
|
||||||
|
const newduration = Math.floor((Date.parse(bandata.data[0].expiryDate?.toString()!) - Date.now()) / 1000 - duration) // (timestamp to freedom - current timestamp) / 1000 (to seconds) - duration
|
||||||
|
try {
|
||||||
|
if (newduration < 3) { // If the target is going to be unbanned in duration + 3 seconds, unban them anyway
|
||||||
|
await tmpapi.moderation.unbanUser(broadcasterId, target)
|
||||||
|
if (MODS.includes(target.name)) remodMod(broadcasterId, target, 0, tmpapi)
|
||||||
|
return { status: true, reason: 'revived' }
|
||||||
|
} else {
|
||||||
|
await tmpapi.moderation.banUser(broadcasterId, { duration: newduration, reason: bandata.data[0].reason!, user: target.id })
|
||||||
|
if (MODS.includes(target.name)) remodMod(broadcasterId, target, newduration * 1000, tmpapi)
|
||||||
|
return { status: true, reason: 'healed' }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return { status: false, reason: 'unknown' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add an entry to the timeouts table */
|
||||||
export async function addTimeoutToDB(attacker: HelixUser, target: HelixUser, source: shooter) {
|
export async function addTimeoutToDB(attacker: HelixUser, target: HelixUser, source: shooter) {
|
||||||
// This has passed the existance check so there's no need to check if the users exist (twitch)
|
// This has passed the existance check so there's no need to check if the users exist (twitch)
|
||||||
const timeoutobj = {
|
const timeoutobj = {
|
||||||
@@ -42,16 +73,17 @@ export async function addTimeoutToDB(attacker: HelixUser, target: HelixUser, sou
|
|||||||
await pb.collection('timeouts').create(timeoutobj)
|
await pb.collection('timeouts').create(timeoutobj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Give the target mod status back after timeout */
|
||||||
function remodMod(broadcasterid: string, target: HelixUser, duration: number, api: ApiClient) {
|
function remodMod(broadcasterid: string, target: HelixUser, duration: number, api: ApiClient) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const bandata = await api.moderation.getBannedUsers(broadcasterid, { userId: target.id })
|
const bandata = await api.moderation.getBannedUsers(broadcasterid, { userId: target.id })
|
||||||
if (bandata.data.length !== 0) {
|
if (bandata.data.length !== 0) {
|
||||||
const timeoutleft = Date.parse(bandata.data[0].expiryDate?.toString()!) - Date.now() + 3000 // date when timeout expires - current date + 3 seconds constant
|
const timeoutleft = Date.parse(bandata.data[0].expiryDate?.toString()!) - Date.now() // date when timeout expires - current date + 3 seconds constant
|
||||||
remodMod(broadcasterid, target, timeoutleft, api) // Call the current function with new time (recursion)
|
remodMod(broadcasterid, target, timeoutleft, api) // Call the current function with new time (recursion)
|
||||||
} else { // If user is still timed out it doesn't try to remod the target
|
} else { // If user is still timed out it doesn't try to remod the target
|
||||||
try {
|
try {
|
||||||
await api.moderation.addModerator(broadcasterid, target)
|
await api.moderation.addModerator(broadcasterid, target.id)
|
||||||
} catch (err) { console.log(err) } // This triggers when the timeout got shortened. try/catch so no runtime error
|
} catch (err) { } // This triggers when the timeout got shortened. try/catch so no runtime error
|
||||||
}
|
}
|
||||||
}, duration + 3000) // callback gets called after duration of timeout + 3 seconds
|
}, duration + 3000) // callback gets called after duration of timeout + 3 seconds
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import pb, { User } from './pocketbase'
|
import pb, { User } from './pocketbase'
|
||||||
import { HelixUser } from '@twurple/api'
|
import { HelixUser } from '@twurple/api'
|
||||||
import itemData from '../items'
|
import itemData, { ITEMS } from '../items'
|
||||||
|
|
||||||
const EMPTYINV = itemData.reduce((acc, item) => {
|
const EMPTYINV = itemData.reduce((acc, item) => {
|
||||||
acc[item.name] = 0
|
acc[item.name] = 0
|
||||||
return acc
|
return acc
|
||||||
}, {} as Record<string, number>)
|
}, {} as Record<string, number>)
|
||||||
|
|
||||||
export type inventory = {
|
export type inventory = {
|
||||||
@@ -16,6 +16,8 @@ type balanceGetResult = {
|
|||||||
data: User
|
data: User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ensures that the user exists in the database
|
||||||
|
* Returns an object with balance as well as the entire user entry from the database */
|
||||||
export async function getBalance(user: HelixUser): Promise<balanceGetResult> {
|
export async function getBalance(user: HelixUser): Promise<balanceGetResult> {
|
||||||
await DBValidation(user)
|
await DBValidation(user)
|
||||||
const data = await pb.collection('users').getFirstListItem(`id="${user!.id}"`)
|
const data = await pb.collection('users').getFirstListItem(`id="${user!.id}"`)
|
||||||
@@ -28,6 +30,7 @@ type balanceChangeResult = {
|
|||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Changes the balance of the current user by the requested amount */
|
||||||
export async function changeBalance(user: HelixUser, amount: number): Promise<balanceChangeResult> {
|
export async function changeBalance(user: HelixUser, amount: number): Promise<balanceChangeResult> {
|
||||||
let { balance, data } = await getBalance(user)
|
let { balance, data } = await getBalance(user)
|
||||||
if (amount < 0 && balance - amount < 0) return { result: false, reason: 'negative', count: balance }
|
if (amount < 0 && balance - amount < 0) return { result: false, reason: 'negative', count: balance }
|
||||||
@@ -49,7 +52,9 @@ interface timeoutsGetResult {
|
|||||||
|
|
||||||
const BLASTERS = ['blaster', 'grenade', 'tnt']
|
const BLASTERS = ['blaster', 'grenade', 'tnt']
|
||||||
|
|
||||||
async function getTimeouts(userId: string, monthdata?: string): Promise<timeoutsGetResult> {
|
/** Get the amount of times the user has (been) shot (by) another user
|
||||||
|
* The 'blaster' data is all timeouts excluding silver bullets */
|
||||||
|
export async function getTimeouts(userId: string, monthdata?: string): Promise<timeoutsGetResult> {
|
||||||
let monthquery = ''
|
let monthquery = ''
|
||||||
if (monthdata) monthquery = ` && created~"${monthdata}"`
|
if (monthdata) monthquery = ` && created~"${monthdata}"`
|
||||||
const hit = await pb.collection('timeouts').getFullList({ filter: `target="${userId}"${monthquery}` })
|
const hit = await pb.collection('timeouts').getFullList({ filter: `target="${userId}"${monthquery}` })
|
||||||
@@ -72,6 +77,8 @@ async function getTimeouts(userId: string, monthdata?: string): Promise<timeouts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the amount of grenades and tnt used by specified user
|
||||||
|
* The monthdata should be something like "2025-01" if specified */
|
||||||
async function getItemUses(userId: string, monthdata?: string): Promise<inventory> {
|
async function getItemUses(userId: string, monthdata?: string): Promise<inventory> {
|
||||||
let monthquery = ''
|
let monthquery = ''
|
||||||
if (monthdata) monthquery = ` && created~"${monthdata}"`
|
if (monthdata) monthquery = ` && created~"${monthdata}"`
|
||||||
@@ -82,6 +89,7 @@ async function getItemUses(userId: string, monthdata?: string): Promise<inventor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the inventory of specific user */
|
||||||
export async function getInventory(user: HelixUser): Promise<inventory> {
|
export async function getInventory(user: HelixUser): Promise<inventory> {
|
||||||
await DBValidation(user)
|
await DBValidation(user)
|
||||||
const data = await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
const data = await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
||||||
@@ -92,6 +100,8 @@ interface statsGetResult extends timeoutsGetResult {
|
|||||||
used: inventory
|
used: inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the hits, shoot and used item stats from specific user
|
||||||
|
* The monthdata should be something like "2025-01" if specified */
|
||||||
export async function getStats(user: HelixUser, monthdata?: string): Promise<statsGetResult> {
|
export async function getStats(user: HelixUser, monthdata?: string): Promise<statsGetResult> {
|
||||||
await DBValidation(user)
|
await DBValidation(user)
|
||||||
const { hit, shot } = await getTimeouts(user.id, monthdata)
|
const { hit, shot } = await getTimeouts(user.id, monthdata)
|
||||||
@@ -99,32 +109,39 @@ export async function getStats(user: HelixUser, monthdata?: string): Promise<sta
|
|||||||
return { hit, shot, used }
|
return { hit, shot, used }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update the inventory of the target user with new inventory data */
|
||||||
export async function updateInventory(user: HelixUser, newinv: inventory) {
|
export async function updateInventory(user: HelixUser, newinv: inventory) {
|
||||||
await DBValidation(user)
|
await DBValidation(user)
|
||||||
const data = await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
const data = await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
||||||
const recordid = data.id
|
const recordid = data.id
|
||||||
await pb.collection('users').update(recordid, { inventory: newinv })
|
await pb.collection('users').update(recordid, { inventory: newinv })
|
||||||
}
|
}
|
||||||
|
/** Creates a new entry in the useditems table */
|
||||||
export async function addUsedItem(user: HelixUser, item: string) {
|
export async function addUsedItem(user: HelixUser, item: string) {
|
||||||
await DBValidation(user)
|
await DBValidation(user)
|
||||||
await pb.collection('itemuses').create({ user: user.id, name: item })
|
await pb.collection('itemuses').create({ user: user.id, name: item })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Validate if the HelixUser has an entry in the database
|
||||||
|
* Add missing inventory items*/
|
||||||
export async function DBValidation(user: HelixUser) {
|
export async function DBValidation(user: HelixUser) {
|
||||||
try {
|
try {
|
||||||
await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
let { inventory } = await pb.collection('users').getFirstListItem(`id="${user.id}"`)
|
||||||
|
if (Object.keys(inventory).sort().toString() === ITEMS.sort().toString()) return
|
||||||
|
ITEMS.forEach(key => {
|
||||||
|
if (!(key in inventory)) inventory[key] = 0
|
||||||
|
})
|
||||||
|
await pb.collection('users').update(user.id, { inventory })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await createUser(user!)
|
await createUser(user!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** Create the user in the database */
|
||||||
async function createUser(user: HelixUser) {
|
async function createUser(user: HelixUser) {
|
||||||
const data = {
|
const data = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
firstname: user.name,
|
firstname: user.name,
|
||||||
inventory: EMPTYINV,
|
inventory: EMPTYINV,
|
||||||
itemuses: EMPTYINV,
|
|
||||||
lastlootbox: "1970-01-01 12:00:00.000Z",
|
lastlootbox: "1970-01-01 12:00:00.000Z",
|
||||||
balance: 0
|
balance: 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user