Files
2026-07-13 13:34:48 +08:00

694 lines
25 KiB
TypeScript

import * as sdk from '@botpress/sdk'
const { z } = sdk
type ActionDefinitions = NonNullable<sdk.IntegrationDefinition['actions']>
type ActionDef = ActionDefinitions[string]
const MAJOR_DIMENSION = ['ROWS', 'COLUMNS'] as const
export type MajorDimension = (typeof MAJOR_DIMENSION)[number]
const _sheetsValue = z
.string()
.title('Stringified value')
.describe('Represents the value of a single cell. This is a stringified number, string or boolean value')
const _commonFields = {
range: z.string().title('Range').placeholder("'Sheet name'!A1:F8"),
majorDimension: z.enum(MAJOR_DIMENSION).title('Major Dimension').default('ROWS'),
values: z
.array(
z
.array(_sheetsValue)
.title('Row or column')
.describe('Represents a major dimension (a row or column) of a values range')
)
.title('Values'),
} as const
const getValues = {
title: 'Get Values',
description: 'Returns the values of a range in the spreadsheet.',
input: {
schema: z.object({
range: _commonFields.range.describe('The A1 notation of the range to retrieve. (e.g. "Sheet1!A1:B2")'),
majorDimension: _commonFields.majorDimension
.optional()
.describe(
'If it equals "ROWS", then the values are returned as rows. If it equals "COLUMNS", then the values are returned as columns.'
),
}),
},
output: {
schema: z.object({
range: _commonFields.range.describe(
'The range the values cover, in A1 notation. This range indicates the entire requested range, even though the values will exclude trailing rows and columns. (e.g. "Sheet1!A1:B2")'
),
majorDimension: _commonFields.majorDimension.describe(
'If it equals "ROWS", then the values are returned in rows. If it equals "COLUMNS", then the values are returned in columns.'
),
values: _commonFields.values.describe(
'The data that was read. This is an array of arrays, the outer array representing all the data and each inner array representing a major dimension (a row or column). Each item in the inner array corresponds with one cell. (e.g. [["a", "b"], ["c", "d"]])'
),
}),
},
} as const satisfies ActionDef
const setValues = {
title: 'Set Values',
description: 'Sets values in a range in the spreadsheet.',
input: {
schema: z.object({
range: _commonFields.range.describe('The A1 notation of the range to update. (e.g. "Sheet1!A1:B2")'),
majorDimension: _commonFields.majorDimension
.optional()
.describe(
'If it equals "ROWS", then the values are inserted as rows. If it equals "COLUMNS", then the values are inserted as columns.'
),
values: _commonFields.values.describe(
'The values to write to the range. This is an array of arrays, where each inner array represents a major dimension (a row or column) of data. (e.g. [["a", "b"], ["c", "d"]])'
),
}),
},
output: {
schema: z.object({
spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'),
updatedRange: z
.string()
.title('Updated Range')
.describe('The range (in A1 notation) that updates were applied to.'),
updatedRows: z
.number()
.title('Updated Rows')
.describe('The number of rows where at least one cell in the row was updated.'),
updatedColumns: z
.number()
.title('Updated Columns')
.describe('The number of columns where at least one cell in the column was updated.'),
updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'),
}),
},
} as const satisfies ActionDef
const appendValues = {
title: 'Append Values',
description:
'Appends values to the spreadsheet. The input startColumn is used to search for existing data and find a "table" within that range. Values will be appended to the next row of the table, starting with the first column of the table.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
startColumn: z
.string()
.title('Start Column')
.describe(
'The start column letter(s) (e.g. "A", "B", "AA"). Used to identify the table in the sheet — data will be appended after the last row of the detected table.'
),
majorDimension: _commonFields.majorDimension
.optional()
.describe(
'If it equals "ROWS", then the values are inserted as rows. If it equals "COLUMNS", then the values are inserted as columns.'
),
values: _commonFields.values.describe(
'The values to write to the range. This is an array of arrays, where each inner array represents a major dimension (a row or column) of data. (e.g. [["a", "b"], ["c", "d"]])'
),
}),
},
output: {
schema: z.object({
spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'),
tableRange: z
.string()
.title('Table Range')
.describe(
'The range (in A1 notation) of the table that values are being appended to (before the values were appended). Empty if no table was found.'
),
updates: z
.object({
spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'),
updatedRange: z
.string()
.title('Updated Range')
.describe('The range (in A1 notation) that updates were applied to.'),
updatedRows: z
.number()
.title('Updated Rows')
.describe('The number of rows where at least one cell in the row was updated.'),
updatedColumns: z
.number()
.title('Updated Columns')
.describe('The number of columns where at least one cell in the column was updated.'),
updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'),
})
.title('Updates')
.describe('The updates that were applied to the spreadsheet.'),
}),
},
} as const satisfies ActionDef
const clearValues = {
title: 'Clear Values',
description:
'Clears values from a spreadsheet. Only values are cleared; all other properties of the cell (such as formatting, data validation, etc..) are kept.',
input: {
schema: z.object({
range: _commonFields.range.describe('The A1 notation of the range to clear. (e.g. "Sheet1!A1:B2")'),
}),
},
output: {
schema: z.object({
spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet the updates were applied to.'),
clearedRange: z.string().title('Cleared Range').describe('The range (in A1 notation) that was cleared.'),
}),
},
} as const satisfies ActionDef
const getInfoSpreadsheet = {
title: 'Get Info of a SpreadSheet',
description: 'Returns the properties and metadata of the specified spreadsheet.',
input: {
schema: z.object({
fields: z
.array(z.string().title('Field name').describe('The field to include in the response'))
.title('Fields')
.describe(
'The fields to include in the response when retrieving spreadsheet properties and metadata. This is a list of field names. (eg. spreadsheetId, properties.title, sheets.properties.sheetId, sheets.properties.title)'
),
}),
},
output: {
schema: z.object({
spreadsheetId: z
.union([z.string(), z.null()])
.title('Spreadsheet ID')
.describe('The unique identifier of the spreadsheet.')
.optional(),
spreadsheetUrl: z
.union([z.string(), z.null()])
.title('Spreadsheet URL')
.describe('The URL of the spreadsheet.')
.optional(),
dataSources: z
.array(z.any())
.describe('The data sources connected to the spreadsheet.')
.title('Data Sources')
.optional(),
dataSourceSchedules: z
.array(z.any())
.describe('The schedules of the data sources.')
.title('Data Source Schedules')
.optional(),
developerMetadata: z
.array(z.any())
.describe('The developer metadata associated with the spreadsheet.')
.title('Developer Metadata')
.optional(),
namedRanges: z
.array(z.any())
.describe('The named ranges defined in the spreadsheet.')
.title('Named Ranges')
.optional(),
properties: z.any().describe('The properties of the spreadsheet.').title('Properties').optional(),
sheets: z.array(z.any()).describe('The sheets present in the spreadsheet.').title('Sheets').optional(),
}),
},
} as const satisfies ActionDef
const addSheet = {
title: 'Add Sheet',
description: 'Adds a new sheet to the spreadsheet.',
input: {
schema: z.object({
title: z.string().title('Title').describe('The title of the new sheet to add to the spreadsheet.'),
}),
},
output: {
schema: z.object({
spreadsheetId: z.string().title('Spreadsheet ID').describe('The spreadsheet ID of the new sheet.'),
newSheet: z
.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the new sheet.'),
title: z.string().title('Title').describe('The title of the new sheet.'),
index: z.number().title('Index').describe('The index of the new sheet within the spreadsheet.'),
isHidden: z.boolean().title('Is Hidden').describe('Whether the new sheet is hidden.'),
})
.title('New Sheet')
.describe('The new sheet that was added to the spreadsheet.'),
}),
},
} as const satisfies ActionDef
const deleteSheet = {
title: 'Delete Sheet',
description: 'Deletes a sheet from the spreadsheet.',
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to delete.'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDef
const renameSheet = {
title: 'Rename Sheet',
description: 'Renames a sheet in the spreadsheet.',
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to rename.'),
newTitle: z.string().title('New Title').describe('The new title of the sheet.'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDef
const getAllSheetsInSpreadsheet = {
title: 'Get All Sheets in Spreadsheet',
description: 'Returns all sheets in the spreadsheet.',
input: {
schema: z.object({}),
},
output: {
schema: z.object({
sheets: z
.array(
z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet.'),
title: z.string().title('Title').describe('The name of the sheet.'),
index: z.number().title('Index').describe('The index of the sheet within the spreadsheet.'),
isHidden: z.boolean().title('Is Hidden').describe('Whether the sheet is hidden.'),
hasProtectedRanges: z
.boolean()
.title('Has Protected Ranges')
.describe('Whether the sheet has protected ranges.'),
isFullyProtected: z.boolean().title('Is Protected').describe('Whether the entire sheet is protected.'),
})
)
.title('Sheets')
.describe('The sheets present in the spreadsheet.'),
}),
},
} as const satisfies ActionDef
const setSheetVisibility = {
title: 'Set Sheet Visibility',
description: 'Sets the visibility of a sheet in the spreadsheet.',
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to set visibility.'),
isHidden: z.boolean().title('Is Hidden').describe('Whether the sheet is hidden.'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDef
const moveSheetHorizontally = {
title: 'Move Sheet Horizontally',
description: 'Moves a sheet to a new index in the spreadsheet.',
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to move.'),
newIndex: z.number().title('New Index').describe('The new index of the sheet within the spreadsheet.'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDef
const protectNamedRange = {
title: 'Protect Named Range',
description: 'Creates a protected range from a named range, preventing modification.',
input: {
schema: z.object({
namedRangeId: z.string().title('Named Range ID').describe('The ID of the named range to protect.'),
warningOnly: z
.boolean()
.title('Warning Only')
.optional()
.describe('Whether the protection displays a warning but still allows editing.'),
requestingUserCanEdit: z
.boolean()
.title('Requesting User Can Edit')
.optional()
.describe('Whether the user adding the protection can edit the protected range.'),
}),
},
output: {
schema: z.object({
protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the new protected range.'),
}),
},
} as const satisfies ActionDef
const getNamedRanges = {
title: 'Get Named Ranges',
description: 'Returns all named ranges in the spreadsheet.',
input: {
schema: z.object({}),
},
output: {
schema: z.object({
namedRanges: z
.array(
z.object({
namedRangeId: z.string().title('Named Range ID').describe('The ID of the named range.'),
name: z.string().title('Name').describe('The name of the named range.'),
range: _commonFields.range.describe('The range of the named range in A1 notation. (e.g. "A1:B2")'),
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet the named range applies to.'),
})
)
.title('Named Ranges')
.describe('The named ranges defined in the spreadsheet.'),
}),
},
} as const satisfies ActionDef
const getProtectedRanges = {
title: 'Get Protected Ranges',
description: 'Returns all protected ranges in the spreadsheet.',
input: {
schema: z.object({}),
},
output: {
schema: z.object({
protectedRanges: z
.array(
z.object({
protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the protected range.'),
namedRangeId: z
.string()
.title('Named Range ID')
.describe('The ID of the named range, if the protected range is backed by a named range.'),
range: _commonFields.range.describe('The range of the protected range in A1 notation. (e.g. "A1:B2")'),
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet the protected range applies to.'),
description: z.string().title('Description').describe('The description of the protected range.'),
warningOnly: z
.boolean()
.title('Warning Only')
.describe('Whether the protection displays a warning but still allows editing.'),
requestingUserCanEdit: z
.boolean()
.title('Requesting User Can Edit')
.describe('Whether the user adding the protection can edit the protected range.'),
})
)
.title('Protected Ranges')
.describe('The protected ranges defined in the spreadsheet.'),
}),
},
} as const satisfies ActionDef
const unprotectRange = {
title: 'Unprotect Range',
description: 'Removes protection from a protected range in the spreadsheet.',
input: {
schema: z.object({
protectedRangeId: z.number().title('Protected Range ID').describe('The ID of the protected range to unprotect.'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDef
const createNamedRangeInSheet = {
title: 'Create Named Range in Sheet',
description: 'Creates a named range in a sheet.',
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to create the named range in.'),
rangeName: z
.string()
.title('Name')
.describe(
'The name of the named range. Must follow naming rules: cannot contain spaces, cannot start with a number, and cannot conflict with standard cell references (e.g., "A1", "R1C1"). Names violating these rules will be considered invalid.'
),
rangeA1: _commonFields.range.describe(
'The A1 notation of the range to associate with the named range. (e.g. "Sheet1!A1:B2")'
),
}),
},
output: {
schema: z.object({
namedRangeId: z.string().title('Named Range ID').describe('The ID of the new named range.'),
}),
},
} as const satisfies ActionDef
const _rowSchema = z
.object({
rowIndex: z.number().title('Row Index').describe('The 1-based index of the row in the sheet.'),
values: z.array(z.string().title('Cell Value')).title('Values').describe('The cell values in the row.'),
})
.title('Row')
.describe('A row with its index and values.')
const findRows = {
title: 'Find Rows',
description:
'Search for rows where a specific column matches a value. Returns all matching rows with their indexes. Handles empty sheets and no matches gracefully by returning an empty array.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
searchColumn: z.string().title('Search Column').describe('The column letter to search in (e.g. "A", "B", "AA").'),
searchValue: z.string().title('Search Value').describe('The value to search for in the specified column.'),
dataRange: z
.string()
.title('Data Range')
.optional()
.describe(
'Optional A1 notation range to limit the search (e.g. "A1:F100"). If not provided, searches the entire sheet.'
),
}),
},
output: {
schema: z.object({
rows: z.array(_rowSchema).title('Matching Rows').describe('The rows that match the search criteria.'),
totalMatches: z.number().title('Total Matches').describe('The total number of matching rows found.'),
}),
},
} as const satisfies ActionDef
const findRow = {
title: 'Find Row (First Match)',
description:
'Search for the first row where a specific column matches a value. Returns the row data and its index, or null if no match is found.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
searchColumn: z.string().title('Search Column').describe('The column letter to search in (e.g. "A", "B", "AA").'),
searchValue: z.string().title('Search Value').describe('The value to search for in the specified column.'),
dataRange: z
.string()
.title('Data Range')
.optional()
.describe(
'Optional A1 notation range to limit the search (e.g. "A1:F100"). If not provided, searches the entire sheet.'
),
}),
},
output: {
schema: z.object({
found: z.boolean().title('Found').describe('Whether a matching row was found.'),
row: _rowSchema.nullable().describe('The first matching row, or null if not found.'),
}),
},
} as const satisfies ActionDef
const getRow = {
title: 'Get Row',
description: 'Fetch a specific row by its 1-based index. Provides direct row access without A1 notation math.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
rowIndex: z
.number()
.title('Row Index')
.describe('The 1-based row index to retrieve (e.g. 1 for the first row, 2 for the second row).'),
startColumn: z
.string()
.title('Start Column')
.optional()
.default('A')
.describe('The starting column letter (e.g. "A"). Defaults to "A".'),
endColumn: z
.string()
.title('End Column')
.optional()
.describe('The ending column letter (e.g. "Z"). If not provided, returns all columns with data.'),
}),
},
output: {
schema: z.object({
found: z.boolean().title('Found').describe('Whether the row exists and has data.'),
row: _rowSchema.nullable().describe('The row data, or null if the row is empty or does not exist.'),
}),
},
} as const satisfies ActionDef
const updateRow = {
title: 'Update Row',
description:
'Update a specific row by its 1-based index with a partial or complete set of values. Only the provided values are updated.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
rowIndex: z.number().title('Row Index').describe('The 1-based row index to update.'),
values: z
.array(z.string().title('Cell Value'))
.title('Values')
.describe('The values to write to the row, starting from the start column.'),
startColumn: z
.string()
.title('Start Column')
.optional()
.default('A')
.describe('The starting column letter for the update (e.g. "A"). Defaults to "A".'),
}),
},
output: {
schema: z.object({
updatedRange: z.string().title('Updated Range').describe('The range (in A1 notation) that was updated.'),
updatedCells: z.number().title('Updated Cells').describe('The number of cells updated.'),
}),
},
} as const satisfies ActionDef
const insertRowAtIndex = {
title: 'Insert Row at Index',
description: 'Insert a new row at a specific 1-based index. Existing rows at and below the index are shifted down.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
rowIndex: z.number().title('Row Index').describe('The 1-based index where the new row should be inserted.'),
values: z
.array(z.string().title('Cell Value'))
.title('Values')
.optional()
.describe('Optional values to populate the new row.'),
startColumn: z
.string()
.title('Start Column')
.optional()
.default('A')
.describe('The starting column letter for the values (e.g. "A"). Defaults to "A".'),
}),
},
output: {
schema: z.object({
insertedRowIndex: z.number().title('Inserted Row Index').describe('The 1-based index of the newly inserted row.'),
}),
},
} as const satisfies ActionDef
const deleteRows = {
title: 'Delete Rows',
description:
'Delete one or more rows by their 1-based indexes. Rows are deleted in reverse order to preserve indexes during deletion.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
rowIndexes: z
.array(z.number().title('Row Index'))
.title('Row Indexes')
.describe('The 1-based row indexes to delete.'),
}),
},
output: {
schema: z.object({
deletedCount: z.number().title('Deleted Count').describe('The number of rows deleted.'),
}),
},
} as const satisfies ActionDef
const upsertRow = {
title: 'Upsert Row',
description:
'Update a row if it exists (based on a key column match), or append a new row if no match is found. Useful for maintaining unique records.',
input: {
schema: z.object({
sheetName: z
.string()
.title('Sheet Name')
.optional()
.describe('The name of the sheet (e.g. "Sheet1"). If not provided, the first visible sheet is used.'),
keyColumn: z
.string()
.title('Key Column')
.describe('The column letter to use for matching (e.g. "A" for ID column).'),
keyValue: z.string().title('Key Value').describe('The value to match in the key column.'),
values: z.array(z.string().title('Cell Value')).title('Values').describe('The values to write to the row.'),
startColumn: z
.string()
.title('Start Column')
.optional()
.default('A')
.describe('The starting column letter for the values (e.g. "A"). Defaults to "A".'),
}),
},
output: {
schema: z.object({
action: z.enum(['updated', 'inserted']).title('Action').describe('Whether the row was updated or inserted.'),
rowIndex: z.number().title('Row Index').describe('The 1-based index of the affected row.'),
}),
},
} as const satisfies ActionDef
export const actions = {
addSheet,
appendValues,
clearValues,
createNamedRangeInSheet,
deleteRows,
deleteSheet,
findRow,
findRows,
getAllSheetsInSpreadsheet,
getInfoSpreadsheet,
getNamedRanges,
getProtectedRanges,
getRow,
getValues,
insertRowAtIndex,
moveSheetHorizontally,
protectNamedRange,
renameSheet,
setSheetVisibility,
unprotectRange,
updateRow,
upsertRow,
setValues,
} as const satisfies ActionDefinitions