feat: config management
fuck it we're vibecoding now
This commit is contained in:
parent
1ea3f3ba97
commit
1eb2200fbe
14 changed files with 1082 additions and 33 deletions
25
src/bot/commands/admin/config-refresh.ts
Normal file
25
src/bot/commands/admin/config-refresh.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import type { BotType } from '@/bot/index.js'
|
||||
import { bold, format } from 'gramio'
|
||||
import { isChatAdmin, refreshAdminCache } from '@/bot/utilities/perms.js'
|
||||
|
||||
export default function (bot: BotType) {
|
||||
// Command to refresh admin cache
|
||||
bot.command('reload', async (ctx) => {
|
||||
if (!['group', 'supergroup'].includes(ctx.chat.type)) {
|
||||
return ctx.reply('This command can only be used in groups or supergroups')
|
||||
}
|
||||
|
||||
// Any admin can refresh cache
|
||||
const isAdmin = await isChatAdmin(ctx.chat.id, ctx.from.id)
|
||||
if (!isAdmin) {
|
||||
return ctx.reply('This command can only be used by admins')
|
||||
}
|
||||
|
||||
// Refresh the admin cache for this chat
|
||||
refreshAdminCache(ctx.chat.id)
|
||||
|
||||
return ctx.reply(
|
||||
format`${bold('Admin cache refreshed!')} Permission checks will use fresh data from Telegram.`,
|
||||
)
|
||||
})
|
||||
}
|
||||
302
src/bot/commands/admin/config.ts
Normal file
302
src/bot/commands/admin/config.ts
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
import type { BotType } from '@/bot/index.js'
|
||||
import { bold, format, InlineKeyboard, italic } from 'gramio'
|
||||
import { isChatAdmin, isChatOwner } from '@/bot/utilities/perms.js'
|
||||
import { ChatConfigService, type RoleManagePermission, type RoleMentionPermission } from '@/shared/services/chatConfig.js'
|
||||
import { PermissionService } from '@/shared/services/permission.js'
|
||||
import { bot } from '@/bot/index.js'
|
||||
|
||||
// Shared keyboard builders for consistent UI
|
||||
const MANAGE_PERMISSION_OPTIONS: { value: RoleManagePermission, label: string }[] = [
|
||||
{ value: 'everyone', label: 'Everyone' },
|
||||
{ value: 'all_admins', label: 'All Admins' },
|
||||
{ value: 'admin_can_promote_members', label: 'Admins: Promote Members' },
|
||||
{ value: 'admin_can_change_info', label: 'Admins: Change Info' },
|
||||
{ value: 'admin_can_manage_chat', label: 'Admins: Manage Chat' },
|
||||
{ value: 'only_owner', label: 'Only Owner' },
|
||||
]
|
||||
|
||||
const MENTION_PERMISSION_OPTIONS: { value: RoleMentionPermission, label: string }[] = [
|
||||
{ value: 'everyone', label: 'Everyone' },
|
||||
{ value: 'all_admins', label: 'All Admins' },
|
||||
{ value: 'only_owner', label: 'Only Owner' },
|
||||
]
|
||||
|
||||
function buildManageKeyboard(currentValue: RoleManagePermission): InlineKeyboard {
|
||||
const keyboard = new InlineKeyboard()
|
||||
for (const option of MANAGE_PERMISSION_OPTIONS) {
|
||||
const isSelected = option.value === currentValue
|
||||
const label = isSelected ? `✓ ${option.label}` : option.label
|
||||
keyboard.text(label, `config:manage:${option.value}`).row()
|
||||
}
|
||||
keyboard.text('« Back to Config', 'config:back')
|
||||
return keyboard
|
||||
}
|
||||
|
||||
function buildMentionKeyboard(currentValue: RoleMentionPermission): InlineKeyboard {
|
||||
const keyboard = new InlineKeyboard()
|
||||
for (const option of MENTION_PERMISSION_OPTIONS) {
|
||||
const isSelected = option.value === currentValue
|
||||
const label = isSelected ? `✓ ${option.label}` : option.label
|
||||
keyboard.text(label, `config:mention:${option.value}`).row()
|
||||
}
|
||||
keyboard.text('« Back to Config', 'config:back')
|
||||
return keyboard
|
||||
}
|
||||
|
||||
function buildMainConfigKeyboard(isOwner: boolean): InlineKeyboard {
|
||||
const keyboard = new InlineKeyboard()
|
||||
if (isOwner) {
|
||||
keyboard.text('⚙️ Role Management', 'config:show:manage').row()
|
||||
keyboard.text('📢 Role Mentions', 'config:show:mention')
|
||||
}
|
||||
return keyboard
|
||||
}
|
||||
|
||||
export default function (bot: BotType) {
|
||||
// Command to show current configuration
|
||||
bot.command('config', async (ctx) => {
|
||||
if (!['group', 'supergroup'].includes(ctx.chat.type)) {
|
||||
return ctx.reply('This command can only be used in groups or supergroups')
|
||||
}
|
||||
|
||||
// Any admin can view config
|
||||
const isAdmin = await isChatAdmin(ctx.chat.id, ctx.from.id)
|
||||
if (!isAdmin) {
|
||||
return ctx.reply('This command can only be used by admins')
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(ctx.chat.id, ctx.from.id)
|
||||
const config = await ChatConfigService.getOrCreate(ctx.chat.id)
|
||||
|
||||
return ctx.reply(
|
||||
format`
|
||||
${bold('Chat Configuration')}
|
||||
|
||||
${bold('Role Management:')}
|
||||
${italic(PermissionService.describeRoleManagePermission(config.roleManagePermission))}
|
||||
|
||||
${bold('Role Mentions:')}
|
||||
${italic(PermissionService.describeRoleMentionPermission(config.roleMentionPermission))}
|
||||
|
||||
${isOwner ? '' : italic('\nOnly the chat owner can change settings.')}
|
||||
`,
|
||||
{
|
||||
reply_markup: buildMainConfigKeyboard(isOwner),
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
// Handle navigation to manage settings
|
||||
bot.callbackQuery('config:show:manage', async (ctx) => {
|
||||
const chatId = ctx.message?.chat.id
|
||||
const messageId = ctx.message?.id
|
||||
if (!chatId || !messageId) {
|
||||
return ctx.answerCallbackQuery({ text: 'Error: Could not determine chat', show_alert: true })
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(chatId, ctx.from.id)
|
||||
if (!isOwner) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Only the chat owner can change settings',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const config = await ChatConfigService.getOrCreate(chatId)
|
||||
|
||||
await bot.api.editMessageText({
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
text: format`
|
||||
${bold('Role Management Permission')}
|
||||
|
||||
Current: ${italic(PermissionService.describeRoleManagePermission(config.roleManagePermission))}
|
||||
|
||||
Select who can add/delete roles:
|
||||
`,
|
||||
reply_markup: buildManageKeyboard(config.roleManagePermission),
|
||||
})
|
||||
|
||||
return ctx.answerCallbackQuery()
|
||||
})
|
||||
|
||||
// Handle navigation to mention settings
|
||||
bot.callbackQuery('config:show:mention', async (ctx) => {
|
||||
const chatId = ctx.message?.chat.id
|
||||
const messageId = ctx.message?.id
|
||||
if (!chatId || !messageId) {
|
||||
return ctx.answerCallbackQuery({ text: 'Error: Could not determine chat', show_alert: true })
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(chatId, ctx.from.id)
|
||||
if (!isOwner) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Only the chat owner can change settings',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const config = await ChatConfigService.getOrCreate(chatId)
|
||||
|
||||
await bot.api.editMessageText({
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
text: format`
|
||||
${bold('Role Mention Permission')}
|
||||
|
||||
Current: ${italic(PermissionService.describeRoleMentionPermission(config.roleMentionPermission))}
|
||||
|
||||
Select who can mention roles:
|
||||
`,
|
||||
reply_markup: buildMentionKeyboard(config.roleMentionPermission),
|
||||
})
|
||||
|
||||
return ctx.answerCallbackQuery()
|
||||
})
|
||||
|
||||
// Handle manage permission selection
|
||||
bot.callbackQuery(/^config:manage:(.+)$/, async (ctx) => {
|
||||
const chatId = ctx.message?.chat.id
|
||||
const messageId = ctx.message?.id
|
||||
if (!chatId || !messageId) {
|
||||
return ctx.answerCallbackQuery({ text: 'Error: Could not determine chat', show_alert: true })
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(chatId, ctx.from.id)
|
||||
if (!isOwner) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Only the chat owner can change this setting',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const permission = ctx.queryPayload.split(':')[2] as RoleManagePermission
|
||||
|
||||
const validOptions: RoleManagePermission[] = [
|
||||
'everyone',
|
||||
'all_admins',
|
||||
'admin_can_promote_members',
|
||||
'admin_can_change_info',
|
||||
'admin_can_manage_chat',
|
||||
'only_owner',
|
||||
]
|
||||
|
||||
if (!validOptions.includes(permission)) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Invalid option',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await ChatConfigService.setRoleManagePermission(chatId, permission)
|
||||
|
||||
if (!updated) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Failed to update configuration',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
await bot.api.editMessageText({
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
text: format`
|
||||
${bold('Role Management Permission')}
|
||||
|
||||
Current: ${italic(PermissionService.describeRoleManagePermission(updated.roleManagePermission))}
|
||||
|
||||
Select who can add/delete roles:
|
||||
`,
|
||||
reply_markup: buildManageKeyboard(updated.roleManagePermission),
|
||||
})
|
||||
|
||||
return ctx.answerCallbackQuery({
|
||||
text: `Updated: ${PermissionService.describeRoleManagePermission(updated.roleManagePermission)}`,
|
||||
})
|
||||
})
|
||||
|
||||
// Handle mention permission selection
|
||||
bot.callbackQuery(/^config:mention:(.+)$/, async (ctx) => {
|
||||
const chatId = ctx.message?.chat.id
|
||||
const messageId = ctx.message?.id
|
||||
if (!chatId || !messageId) {
|
||||
return ctx.answerCallbackQuery({ text: 'Error: Could not determine chat', show_alert: true })
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(chatId, ctx.from.id)
|
||||
if (!isOwner) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Only the chat owner can change this setting',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const permission = ctx.queryPayload.split(':')[2] as RoleMentionPermission
|
||||
|
||||
const validOptions: RoleMentionPermission[] = ['everyone', 'all_admins', 'only_owner']
|
||||
|
||||
if (!validOptions.includes(permission)) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Invalid option',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await ChatConfigService.setRoleMentionPermission(chatId, permission)
|
||||
|
||||
if (!updated) {
|
||||
return ctx.answerCallbackQuery({
|
||||
text: 'Failed to update configuration',
|
||||
show_alert: true,
|
||||
})
|
||||
}
|
||||
|
||||
await bot.api.editMessageText({
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
text: format`
|
||||
${bold('Role Mention Permission')}
|
||||
|
||||
Current: ${italic(PermissionService.describeRoleMentionPermission(updated.roleMentionPermission))}
|
||||
|
||||
Select who can mention roles:
|
||||
`,
|
||||
reply_markup: buildMentionKeyboard(updated.roleMentionPermission),
|
||||
})
|
||||
|
||||
return ctx.answerCallbackQuery({
|
||||
text: `Updated: ${PermissionService.describeRoleMentionPermission(updated.roleMentionPermission)}`,
|
||||
})
|
||||
})
|
||||
|
||||
// Handle back button to main config
|
||||
bot.callbackQuery('config:back', async (ctx) => {
|
||||
const chatId = ctx.message?.chat.id
|
||||
const messageId = ctx.message?.id
|
||||
if (!chatId || !messageId) {
|
||||
return ctx.answerCallbackQuery({ text: 'Error: Could not determine chat', show_alert: true })
|
||||
}
|
||||
|
||||
const isOwner = await isChatOwner(chatId, ctx.from.id)
|
||||
const config = await ChatConfigService.getOrCreate(chatId)
|
||||
|
||||
await bot.api.editMessageText({
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
text: format`
|
||||
${bold('Chat Configuration')}
|
||||
|
||||
${bold('Role Management:')}
|
||||
${italic(PermissionService.describeRoleManagePermission(config.roleManagePermission))}
|
||||
|
||||
${bold('Role Mentions:')}
|
||||
${italic(PermissionService.describeRoleMentionPermission(config.roleMentionPermission))}
|
||||
|
||||
${isOwner ? '' : italic('\nOnly the chat owner can change settings.')}
|
||||
`,
|
||||
reply_markup: buildMainConfigKeyboard(isOwner),
|
||||
})
|
||||
|
||||
return ctx.answerCallbackQuery()
|
||||
})
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { BotType } from '@/bot/index.js'
|
||||
import { bold, code, format, italic } from 'gramio'
|
||||
import { RESERVED_NAMES, ROLE_NAME_REGEX } from '@/bot/utilities/constants.js'
|
||||
import { isChatAdmin } from '@/bot/utilities/perms.js'
|
||||
import { PermissionService } from '@/shared/services/permission.js'
|
||||
import { RoleService } from '@/shared/services/role.js'
|
||||
|
||||
export default function (bot: BotType) {
|
||||
|
|
@ -10,9 +10,9 @@ export default function (bot: BotType) {
|
|||
return ctx.reply('This command can only be used in groups or supergroups')
|
||||
}
|
||||
|
||||
const isAdmin = await isChatAdmin(ctx.chat.id, ctx.from.id)
|
||||
if (!isAdmin) {
|
||||
return ctx.reply('this command can only be used by admins')
|
||||
const canManage = await PermissionService.canManageRoles(ctx.chat.id, ctx.from.id)
|
||||
if (!canManage) {
|
||||
return ctx.reply('You don\'t have permission to manage roles')
|
||||
}
|
||||
|
||||
let [slug] = (ctx.args ?? '').split(' ')
|
||||
|
|
@ -45,9 +45,9 @@ export default function (bot: BotType) {
|
|||
await bot.api.setMyCommands({
|
||||
scope: {
|
||||
chat_id: ctx.chatId,
|
||||
type: 'chat'
|
||||
type: 'chat',
|
||||
},
|
||||
commands: roles.map((role) => ({
|
||||
commands: roles.map(role => ({
|
||||
command: role.slug,
|
||||
description: `Mention all members of this role`,
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { BotType } from "@/bot/index.js"
|
||||
import { isChatAdmin } from "@/bot/utilities/perms.js"
|
||||
import { RoleService } from "@/shared/services/role.js"
|
||||
import { bold, code, format } from "gramio"
|
||||
import type { BotType } from '@/bot/index.js'
|
||||
import { bold, code, format } from 'gramio'
|
||||
import { PermissionService } from '@/shared/services/permission.js'
|
||||
import { RoleService } from '@/shared/services/role.js'
|
||||
|
||||
export default (bot: BotType) => {
|
||||
bot.command('roledel', async (ctx) => {
|
||||
|
|
@ -9,9 +9,9 @@ export default (bot: BotType) => {
|
|||
return ctx.reply('this command can only be used in groups or supergroups')
|
||||
}
|
||||
|
||||
const isAdmin = await isChatAdmin(ctx.chat.id, ctx.from.id)
|
||||
if (!isAdmin) {
|
||||
return ctx.reply('this command can only be used by admins')
|
||||
const canManage = await PermissionService.canManageRoles(ctx.chat.id, ctx.from.id)
|
||||
if (!canManage) {
|
||||
return ctx.reply('You don\'t have permission to manage roles')
|
||||
}
|
||||
|
||||
let [roleSlug] = (ctx.args ?? '').trim().split(' ')
|
||||
|
|
@ -27,7 +27,7 @@ export default (bot: BotType) => {
|
|||
}
|
||||
|
||||
const members = await RoleService.getMemberIds(roleSlug, ctx.chatId)
|
||||
await Promise.all(members.map((member) => RoleService.removeMember(role.id!, member)))
|
||||
await Promise.all(members.map(member => RoleService.removeMember(role.id!, member)))
|
||||
|
||||
const deletedRole = await RoleService.delete(role.id!)
|
||||
if (!deletedRole) {
|
||||
|
|
@ -36,18 +36,18 @@ export default (bot: BotType) => {
|
|||
|
||||
setImmediate(async () => {
|
||||
const roles = await RoleService.getByChatId(ctx.chatId)
|
||||
await bot.api.setMyCommands({
|
||||
scope: {
|
||||
chat_id: ctx.chatId,
|
||||
type: 'chat'
|
||||
},
|
||||
commands: roles.map((role) => ({
|
||||
command: role.slug,
|
||||
description: `Mention all members of this role`,
|
||||
})),
|
||||
})
|
||||
await bot.api.setMyCommands({
|
||||
scope: {
|
||||
chat_id: ctx.chatId,
|
||||
type: 'chat',
|
||||
},
|
||||
commands: roles.map(role => ({
|
||||
command: role.slug,
|
||||
description: `Mention all members of this role`,
|
||||
})),
|
||||
})
|
||||
})
|
||||
|
||||
return ctx.reply(format`role ${bold(role.slug)} deleted`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue