A work-in-progress chat bot for Streamplace with chat overlay functionality
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

commandHandler now checks for required and type

+87 -13
+1 -1
lexicons/online/timtinkers/bot/command.json
··· 114 114 "required": { 115 115 "type": "boolean", 116 116 "description": "Whether this parameter must be provided", 117 - "default": true 117 + "default": false 118 118 }, 119 119 "description": { 120 120 "type": "string",
+84 -10
utils/commandHandler.ts
··· 76 76 ): CommandWrapper | null { 77 77 const placeholderPattern = (name: string) => 78 78 new RegExp(`\\{${name}\\}`, "g"); 79 - const anyPlaceholder = /\{([^}]+)\}/g; 80 79 81 80 switch (commandRecord.commandType) { 82 81 case "online.timtinkers.bot.command#simpleCommand": ··· 88 87 }; 89 88 90 89 case "online.timtinkers.bot.command#parameterizedCommand": 91 - return async (_event, params, bot) => { 92 - const placeholders = 93 - commandRecord.responseTemplate!.match(anyPlaceholder) ?? 94 - []; 95 - const response = placeholders.reduce( 96 - (template, placeholder, i) => 97 - template.replace(placeholder, params[i] || ""), 98 - commandRecord.responseTemplate!, 99 - ); 90 + return async (event, params, bot) => { 91 + const parameters = commandRecord.parameters ?? []; 92 + 93 + const resolveParam = async ( 94 + param: OnlineTimtinkersBotCommand.Parameter, 95 + value: string | undefined, 96 + index: number, 97 + ): Promise<string | null> => { 98 + if (value !== undefined) { 99 + if ( 100 + param.type === "number" && isNaN(Number(value)) 101 + ) { 102 + await bot.sendMessage( 103 + `Parameter "${param.name}" must be a number.`, 104 + ); 105 + return null; 106 + } 107 + return value; 108 + } 109 + 110 + // No value provided 111 + if (param.required) { 112 + const typeHint = param.type === "handle" 113 + ? "a handle (e.g. @user.bsky.social)" 114 + : param.type === "number" 115 + ? "a number" 116 + : "a value"; 117 + await bot.sendMessage( 118 + `Parameter "${param.name}" is required and must be ${typeHint}.`, 119 + ); 120 + return null; 121 + } 122 + 123 + // Optional param: index 0 falls back to sender's handle 124 + if (index === 0) { 125 + return `@${ 126 + (await didResolver.resolve(event.did)).handle 127 + }`; 128 + } 129 + 130 + return `{${param.name}}`; 131 + }; 132 + 133 + let response = commandRecord.responseTemplate!; 134 + for (let i = 0; i < parameters.length; i++) { 135 + const param = parameters[i]; 136 + const resolved = await resolveParam( 137 + param, 138 + params[i], 139 + i, 140 + ); 141 + if (resolved === null) return; 142 + response = response.replace( 143 + placeholderPattern(param.name), 144 + resolved, 145 + ); 146 + } 147 + 100 148 const { text, facets } = await buildRichtext(response); 101 149 await bot.sendMessage(text, facets); 102 150 }; 103 151 104 152 case "online.timtinkers.bot.command#rngCommand": 105 153 return async (event, params, bot) => { 154 + if (commandRecord.rngParameter) { 155 + const param = commandRecord.rngParameter; 156 + const value = params[0]; 157 + 158 + if (value !== undefined) { 159 + if ( 160 + param.type === "number" && isNaN(Number(value)) 161 + ) { 162 + await bot.sendMessage( 163 + `Parameter "${param.name}" must be a number.`, 164 + ); 165 + return; 166 + } 167 + } else if (param.required) { 168 + const typeHint = param.type === "handle" 169 + ? "a handle (e.g. @alice.bsky.social)" 170 + : param.type === "number" 171 + ? "a number" 172 + : "a value"; 173 + await bot.sendMessage( 174 + `Parameter "${param.name}" is required and must be ${typeHint}.`, 175 + ); 176 + return; 177 + } 178 + } 179 + 106 180 const result = Math.floor( 107 181 Math.random() * 108 182 (commandRecord.rngMax! - commandRecord.rngMin! + 1),
+2 -2
utils/lexicons/types/online/timtinkers/bot/command.ts
··· 120 120 ]), 121 121 /** 122 122 * Whether this parameter must be provided 123 - * @default true 123 + * @default false 124 124 */ 125 - required: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean(), true), 125 + required: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean(), false), 126 126 /** 127 127 * Expected parameter type for validation 128 128 */