11import { ChannelType , ForumChannel } from 'discord.js' ;
2- import { GetForumChannelsSchema , CreateForumPostSchema , GetForumPostSchema , ListForumThreadsSchema , ReplyToForumSchema , DeleteForumPostSchema } from '../schemas.js' ;
2+ import { GetForumChannelsSchema , CreateForumPostSchema , GetForumPostSchema , ListForumThreadsSchema , ReplyToForumSchema , DeleteForumPostSchema , GetForumTagsSchema , UpdateForumPostSchema } from '../schemas.js' ;
33import { ToolHandler } from './types.js' ;
44import { handleDiscordError } from "../errorHandler.js" ;
55
@@ -280,7 +280,7 @@ export const replyToForumHandler: ToolHandler = async (args, { client }) => {
280280
281281export const deleteForumPostHandler : ToolHandler = async ( args , { client } ) => {
282282 const { threadId, reason } = DeleteForumPostSchema . parse ( args ) ;
283-
283+
284284 try {
285285 if ( ! client . isReady ( ) ) {
286286 return {
@@ -301,12 +301,121 @@ export const deleteForumPostHandler: ToolHandler = async (args, { client }) => {
301301 await thread . delete ( reason || "Forum post deleted via API" ) ;
302302
303303 return {
304- content : [ {
305- type : "text" ,
306- text : `Successfully deleted forum post/thread with ID: ${ threadId } `
304+ content : [ {
305+ type : "text" ,
306+ text : `Successfully deleted forum post/thread with ID: ${ threadId } `
307+ } ]
308+ } ;
309+ } catch ( error ) {
310+ return handleDiscordError ( error ) ;
311+ }
312+ } ;
313+
314+ export const getForumTagsHandler : ToolHandler = async ( args , { client } ) => {
315+ const { forumChannelId } = GetForumTagsSchema . parse ( args ) ;
316+
317+ try {
318+ if ( ! client . isReady ( ) ) {
319+ return {
320+ content : [ { type : "text" , text : "Discord client not logged in." } ] ,
321+ isError : true
322+ } ;
323+ }
324+
325+ const channel = await client . channels . fetch ( forumChannelId ) ;
326+ if ( ! channel || channel . type !== ChannelType . GuildForum ) {
327+ return {
328+ content : [ { type : "text" , text : `Channel ID ${ forumChannelId } is not a forum channel.` } ] ,
329+ isError : true
330+ } ;
331+ }
332+
333+ const forumChannel = channel as ForumChannel ;
334+ const tags = forumChannel . availableTags . map ( tag => ( {
335+ id : tag . id ,
336+ name : tag . name ,
337+ moderated : tag . moderated ,
338+ emoji : tag . emoji ? ( tag . emoji . name || tag . emoji . id ) : null
339+ } ) ) ;
340+
341+ return {
342+ content : [ { type : "text" , text : JSON . stringify ( tags , null , 2 ) } ]
343+ } ;
344+ } catch ( error ) {
345+ return handleDiscordError ( error ) ;
346+ }
347+ } ;
348+
349+ export const updateForumPostHandler : ToolHandler = async ( args , { client } ) => {
350+ const { threadId, name, tags, archived, locked } = UpdateForumPostSchema . parse ( args ) ;
351+
352+ try {
353+ if ( ! client . isReady ( ) ) {
354+ return {
355+ content : [ { type : "text" , text : "Discord client not logged in." } ] ,
356+ isError : true
357+ } ;
358+ }
359+
360+ const thread = await client . channels . fetch ( threadId ) ;
361+ if ( ! thread || ! thread . isThread ( ) ) {
362+ return {
363+ content : [ { type : "text" , text : `Cannot find thread with ID: ${ threadId } ` } ] ,
364+ isError : true
365+ } ;
366+ }
367+
368+ const editOptions : any = { } ;
369+ if ( name !== undefined ) editOptions . name = name ;
370+ if ( archived !== undefined ) editOptions . archived = archived ;
371+ if ( locked !== undefined ) editOptions . locked = locked ;
372+
373+ // Resolve tag names to IDs if tags are provided
374+ if ( tags !== undefined ) {
375+ const parent = thread . parent ;
376+ if ( parent && parent . type === ChannelType . GuildForum ) {
377+ const forumChannel = parent as ForumChannel ;
378+ const availableTags = forumChannel . availableTags ;
379+ const resolved : string [ ] = [ ] ;
380+ const invalid : string [ ] = [ ] ;
381+ for ( const tagInput of tags ) {
382+ const byName = availableTags . find ( t => t . name === tagInput ) ;
383+ if ( byName ) { resolved . push ( byName . id ) ; continue ; }
384+ const byId = availableTags . find ( t => t . id === tagInput ) ;
385+ if ( byId ) { resolved . push ( byId . id ) ; continue ; }
386+ invalid . push ( tagInput ) ;
387+ }
388+ if ( invalid . length > 0 ) {
389+ const validNames = availableTags . map ( t => t . name ) . join ( ', ' ) ;
390+ return {
391+ content : [ { type : "text" , text : `Unknown tag(s): ${ invalid . join ( ', ' ) } . Available tags: ${ validNames } ` } ] ,
392+ isError : true
393+ } ;
394+ }
395+ editOptions . appliedTags = resolved ;
396+ } else {
397+ return {
398+ content : [ { type : "text" , text : `Thread's parent channel is not a forum channel. Tags can only be applied to forum posts.` } ] ,
399+ isError : true
400+ } ;
401+ }
402+ }
403+
404+ const updated = await thread . edit ( editOptions ) ;
405+
406+ const changes : string [ ] = [ ] ;
407+ if ( name !== undefined ) changes . push ( `name → "${ name } "` ) ;
408+ if ( tags !== undefined ) changes . push ( `tags → [${ tags . join ( ', ' ) } ]` ) ;
409+ if ( archived !== undefined ) changes . push ( `archived → ${ archived } ` ) ;
410+ if ( locked !== undefined ) changes . push ( `locked → ${ locked } ` ) ;
411+
412+ return {
413+ content : [ {
414+ type : "text" ,
415+ text : `Successfully updated forum post ${ threadId } : ${ changes . join ( ', ' ) } `
307416 } ]
308417 } ;
309418 } catch ( error ) {
310419 return handleDiscordError ( error ) ;
311420 }
312- } ;
421+ } ;
0 commit comments