mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-19 08:41:39 +01:00
allow mods to be timed out, optional broadcasterAPI added
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
CLIENT_ID=
|
CLIENT_ID=
|
||||||
CLIENT_SECRET=
|
CLIENT_SECRET=
|
||||||
OAUTH_CODE=
|
OAUTH_CODE=
|
||||||
|
DIFFERENT_BROADCASTER=false
|
||||||
|
BROADCASTER_OAUTH_CODE=
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
# These are only needed at first start. These are the values used to login to the admin panel.
|
# These are only needed at first start. These are the values used to login to the admin panel.
|
||||||
# If left empty the email will be set to test@example.com and the password to 1234567890
|
# If left empty the email will be set to test@example.com and the password to 1234567890
|
||||||
#- EMAIL=
|
- EMAIL=
|
||||||
#- PASSWORD=
|
- PASSWORD=
|
||||||
bot:
|
bot:
|
||||||
depends_on:
|
depends_on:
|
||||||
pocketbase:
|
pocketbase:
|
||||||
@@ -28,6 +28,11 @@ services:
|
|||||||
- action: rebuild
|
- action: rebuild
|
||||||
path: ./src
|
path: ./src
|
||||||
environment:
|
environment:
|
||||||
|
# These env variables can be removed once the bot has sucessfully run once
|
||||||
- CLIENT_ID=$CLIENT_ID
|
- CLIENT_ID=$CLIENT_ID
|
||||||
- CLIENT_SECRET=$CLIENT_SECRET
|
- CLIENT_SECRET=$CLIENT_SECRET
|
||||||
- OAUTH_CODE=$OAUTH_CODE
|
- OAUTH_CODE=$OAUTH_CODE
|
||||||
|
# If the broadcaster is different from the bot user,
|
||||||
|
# the broadcaster will need to authorize the bot to perform certain actions
|
||||||
|
- DIFFERENT_BROADCASTER=$DIFFERENT_BROADCASTER
|
||||||
|
- BROADCASTER_OAUTH_CODE=$BROADCASTER_OAUTH_CODE
|
||||||
|
|||||||
@@ -166,6 +166,15 @@ migrate(app => {
|
|||||||
"required": true,
|
"required": true,
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "json"
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "bool3207122276",
|
||||||
|
"name": "main",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "bool"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"indexes": [],
|
"indexes": [],
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const bot = new Bot({
|
|||||||
})
|
})
|
||||||
|
|
||||||
bot.onConnect(async ()=> {
|
bot.onConnect(async ()=> {
|
||||||
console.log("Ready to accept commands!")
|
// await authProvider.refreshAccessTokenForUser(238377856)
|
||||||
await authProvider.refreshAccessTokenForUser(238377856)
|
setTimeout(() => {
|
||||||
})
|
console.log('Bot is ready to accept commands!')
|
||||||
|
}, 1000 * 3)
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import authProvider from "../lib/auth";
|
import authProvider, { broadcasterAuthProvider as BCAuthFunction } from "../lib/auth";
|
||||||
import { ApiClient } from "@twurple/api";
|
import { ApiClient } from "@twurple/api";
|
||||||
const api = new ApiClient({ authProvider })
|
const api = new ApiClient({ authProvider })
|
||||||
export default api
|
export default api
|
||||||
|
const broadcasterApi = BCAuthFunction !== undefined ? new ApiClient({ authProvider: await BCAuthFunction() }) : undefined
|
||||||
|
export { broadcasterApi }
|
||||||
|
|||||||
@@ -1,25 +1,47 @@
|
|||||||
import { RefreshingAuthProvider, exchangeCode } from '@twurple/auth'
|
import { RefreshingAuthProvider, exchangeCode } from '@twurple/auth'
|
||||||
import pb from './pocketbase'
|
import pb from './pocketbase'
|
||||||
|
import { RecordModel } from 'pocketbase'
|
||||||
|
|
||||||
const ttvauth = await pb.collection('ttvauth').getFullList()
|
let ttvauth: RecordModel | undefined
|
||||||
|
try {
|
||||||
|
ttvauth = await pb.collection('ttvauth').getFirstListItem('main=true')
|
||||||
|
} catch (err) {
|
||||||
|
ttvauth = undefined
|
||||||
|
}
|
||||||
|
|
||||||
let auth = ttvauth.length === 0 ? await firstAccess() : ttvauth[0].auth
|
let auth = !ttvauth ? await firstAccess() : ttvauth.auth
|
||||||
|
|
||||||
async function firstAccess() {
|
let broadcasterAuthData: RecordModel | undefined
|
||||||
|
try {
|
||||||
|
broadcasterAuthData = await pb.collection('ttvauth').getFirstListItem('main=false')
|
||||||
|
} catch (err) {
|
||||||
|
broadcasterAuthData = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIFFERENT_BROADCASTER = process.env.DIFFERENT_BROADCASTER
|
||||||
|
broadcasterAuthData = !broadcasterAuthData && DIFFERENT_BROADCASTER === 'true' ? await firstAccess(false) : broadcasterAuthData ? broadcasterAuthData.auth : undefined
|
||||||
|
|
||||||
|
async function firstAccess(main = true) {
|
||||||
// This function gets the required auth codes, and stores it in pocketbase
|
// This function gets the required auth codes, and stores it in pocketbase
|
||||||
// The environment variables can be dropped after first run
|
// The environment variables can be dropped after first run
|
||||||
const CLIENT_ID = process.env.CLIENT_ID
|
const CLIENT_ID = process.env.CLIENT_ID
|
||||||
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
|
||||||
|
|
||||||
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 (!OAUTH_CODE) {
|
if ((main && !OAUTH_CODE) || (!main && !BROADCASTER_OAUTH_CODE)) {
|
||||||
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.")
|
if (main) {
|
||||||
console.error(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=http://localhost&response_type=code&scope=chat:read+chat:edit+moderator:manage:banned_users+moderation:read`)
|
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=http://localhost&response_type=code&scope=chat:read+chat:edit+moderator:manage:banned_users+moderation:read`)
|
||||||
|
} 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(`https://id.twitch.tv/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=http://localhost&response_type=code&scope=moderator:manage:banned_users+moderation:read+channel:manage:moderators`)
|
||||||
|
}
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
const tokens = await exchangeCode(CLIENT_ID, CLIENT_SECRET, OAUTH_CODE, "http://localhost")
|
const tokens = await exchangeCode(CLIENT_ID, CLIENT_SECRET, main ? OAUTH_CODE! : BROADCASTER_OAUTH_CODE!, "http://localhost")
|
||||||
const auth = {
|
const auth = {
|
||||||
CLIENT_ID,
|
CLIENT_ID,
|
||||||
CLIENT_SECRET,
|
CLIENT_SECRET,
|
||||||
@@ -29,7 +51,7 @@ async function firstAccess() {
|
|||||||
OBTAINMENTTIMESTAMP: tokens.obtainmentTimestamp
|
OBTAINMENTTIMESTAMP: tokens.obtainmentTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
await pb.collection('ttvauth').create({ auth })
|
await pb.collection('ttvauth').create({ auth, main })
|
||||||
|
|
||||||
return auth
|
return auth
|
||||||
}
|
}
|
||||||
@@ -54,10 +76,37 @@ authProvider.onRefresh(async (_id, newTokenData) => {
|
|||||||
auth.EXPIRESIN = newTokenData.expiresIn!
|
auth.EXPIRESIN = newTokenData.expiresIn!
|
||||||
auth.OBTAINMENTTIMESTAMP = newTokenData.obtainmentTimestamp
|
auth.OBTAINMENTTIMESTAMP = newTokenData.obtainmentTimestamp
|
||||||
|
|
||||||
const ttvauthid = await pb.collection('ttvauth').getFullList()
|
const ttvauthid = await pb.collection('ttvauth').getFirstListItem('main=true')
|
||||||
await pb.collection('ttvauth').update(ttvauthid[0].id, { auth })
|
await pb.collection('ttvauth').update(ttvauthid.id, { auth })
|
||||||
|
|
||||||
console.log("Refreshed OAuth tokens.")
|
console.log("Refreshed OAuth tokens.")
|
||||||
})
|
})
|
||||||
|
|
||||||
export default authProvider
|
export default authProvider
|
||||||
|
|
||||||
|
const broadcasterAuthProvider = broadcasterAuthData === undefined ? undefined : async () => {
|
||||||
|
const broadcasterAuthProvider = new RefreshingAuthProvider({
|
||||||
|
clientId: broadcasterAuthData.CLIENT_ID,
|
||||||
|
clientSecret: broadcasterAuthData.CLIENT_SECRET
|
||||||
|
})
|
||||||
|
await broadcasterAuthProvider.addUserForToken({
|
||||||
|
accessToken: broadcasterAuthData.ACCESS_TOKEN,
|
||||||
|
refreshToken: broadcasterAuthData.REFRESH_TOKEN,
|
||||||
|
expiresIn: broadcasterAuthData.EXPIRESIN,
|
||||||
|
obtainmentTimestamp: broadcasterAuthData.OBTAINMENTTIMESTAMP
|
||||||
|
}, ['moderator:manage:banned_users', 'moderation:read', 'channel:manage:moderators'])
|
||||||
|
|
||||||
|
broadcasterAuthProvider.onRefresh(async (_id, newTokenData) => {
|
||||||
|
broadcasterAuthData.ACCESS_TOKEN = newTokenData.accessToken
|
||||||
|
broadcasterAuthData.REFRESH_TOKEN = newTokenData.refreshToken!
|
||||||
|
broadcasterAuthData.EXPIRESIN = newTokenData.expiresIn!
|
||||||
|
broadcasterAuthData.OBTAINMENTTIMESTAMP = newTokenData.obtainmentTimestamp
|
||||||
|
|
||||||
|
const broadcasterauthid = await pb.collection('ttvauth').getFirstListItem("main=false")
|
||||||
|
await pb.collection('ttvauth').update(broadcasterauthid.id, { auth: broadcasterAuthData })
|
||||||
|
console.log("Refreshed Broadcaster OAuth tokens.")
|
||||||
|
})
|
||||||
|
return broadcasterAuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
export { broadcasterAuthProvider }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { HelixUser } from "@twurple/api";
|
import { HelixUser } from "@twurple/api";
|
||||||
import api from "./api";
|
import api, { broadcasterApi } from "./api";
|
||||||
import pb from "./pocketbase";
|
import pb from "./pocketbase";
|
||||||
import { getDBID } from "./userHelper";
|
import { getDBID } from "./userHelper";
|
||||||
|
|
||||||
@@ -7,16 +7,29 @@ type shooter = 'blaster' | 'grenade' | 'silverbullet' | 'watergun' | 'tnt'
|
|||||||
|
|
||||||
interface statusmessage {
|
interface statusmessage {
|
||||||
status: boolean,
|
status: boolean,
|
||||||
reason?: string
|
reason: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function timeout(broadcasterid: string, target: HelixUser, duration: number, reason: string): Promise<statusmessage> {
|
export async function timeout(broadcasterid: string, target: HelixUser, duration: number, reason: string): Promise<statusmessage> {
|
||||||
if (!target) return { status: false, reason: 'noexist' }
|
if (!target) return { status: false, reason: 'noexist' }
|
||||||
if (await api.moderation.checkUserBan(broadcasterid, target)) return { status: false, reason: 'banned' }
|
// if (target.name === 'qwerinope') return { status: false, reason: 'unknown' }
|
||||||
|
if (broadcasterApi) {
|
||||||
|
if (await broadcasterApi.moderation.checkUserBan(broadcasterid, target)) return { status: false, reason: 'banned' }
|
||||||
|
} else {
|
||||||
|
if (await api.moderation.checkUserBan(broadcasterid, target)) return { status: false, reason: 'banned' }
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.moderation.banUser(broadcasterid, { duration, reason, user: target })
|
if (broadcasterApi) {
|
||||||
return { status: true }
|
if (await broadcasterApi.moderation.checkUserMod(broadcasterid, target)) {
|
||||||
|
await broadcasterApi.moderation.removeModerator(broadcasterid, target)
|
||||||
|
remodMod(broadcasterid, target, duration)
|
||||||
|
}
|
||||||
|
await broadcasterApi.moderation.banUser(broadcasterid, { duration, reason, user: target })
|
||||||
|
} else {
|
||||||
|
await api.moderation.banUser(broadcasterid, { duration, reason, user: target })
|
||||||
|
}
|
||||||
|
return { status: true, reason: '' }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
return { status: false, reason: 'unknown' }
|
return { status: false, reason: 'unknown' }
|
||||||
@@ -37,3 +50,9 @@ export async function addTimeoutToDB(attacker: HelixUser, target: HelixUser, sou
|
|||||||
}
|
}
|
||||||
await pb.collection('timeouts').create(timeoutobj)
|
await pb.collection('timeouts').create(timeoutobj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function remodMod(broadcasterid: string, target: HelixUser, duration: number) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
await broadcasterApi?.moderation.addModerator(broadcasterid, target)
|
||||||
|
}, (duration + 3) * 1000)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user