@@ -192,6 +192,7 @@ function createFeishuBotRuntime(overrides: DeepPartial<PluginRuntime> = {}): Plu
192192 withReplyDispatcher : withReplyDispatcherMock as never ,
193193 } ,
194194 commands : {
195+ isControlCommandMessage : vi . fn ( ( ) => false ) ,
195196 shouldComputeCommandAuthorized : vi . fn ( ( ) => false ) ,
196197 resolveCommandAuthorizedFromAuthorizers : vi . fn ( ( ) => false ) ,
197198 } ,
@@ -1082,6 +1083,9 @@ describe("handleFeishuMessage command authorization", () => {
10821083 withReplyDispatcher : mockWithReplyDispatcher as never ,
10831084 } ,
10841085 commands : {
1086+ isControlCommandMessage : vi . fn (
1087+ ( body ?: string ) => typeof body === "string" && body . trim ( ) . startsWith ( "/" ) ,
1088+ ) ,
10851089 shouldComputeCommandAuthorized : mockShouldComputeCommandAuthorized ,
10861090 resolveCommandAuthorizedFromAuthorizers : mockResolveCommandAuthorizedFromAuthorizers ,
10871091 } ,
@@ -1149,6 +1153,94 @@ describe("handleFeishuMessage command authorization", () => {
11491153 } ) ;
11501154 } ) ;
11511155
1156+ it ( "marks authorized Feishu text slash commands as text command turns" , async ( ) => {
1157+ mockShouldComputeCommandAuthorized . mockReturnValue ( true ) ;
1158+
1159+ const cfg : ClawdbotConfig = {
1160+ channels : {
1161+ feishu : {
1162+ dmPolicy : "open" ,
1163+ allowFrom : [ "ou-admin" ] ,
1164+ } ,
1165+ } ,
1166+ } as ClawdbotConfig ;
1167+
1168+ const event : FeishuMessageEvent = {
1169+ sender : {
1170+ sender_id : {
1171+ open_id : "ou-admin" ,
1172+ } ,
1173+ } ,
1174+ message : {
1175+ message_id : "msg-text-command-turn" ,
1176+ chat_id : "oc-dm" ,
1177+ chat_type : "p2p" ,
1178+ message_type : "text" ,
1179+ content : JSON . stringify ( { text : "/status" } ) ,
1180+ } ,
1181+ } ;
1182+
1183+ await dispatchMessage ( { cfg, event } ) ;
1184+
1185+ const context = mockCallArg < {
1186+ CommandAuthorized ?: boolean ;
1187+ CommandBody ?: string ;
1188+ CommandTurn ?: unknown ;
1189+ } > ( mockFinalizeInboundContext , 0 , 0 ) ;
1190+ expect ( context . CommandBody ) . toBe ( "/status" ) ;
1191+ expect ( context . CommandAuthorized ) . toBe ( true ) ;
1192+ expect ( context . CommandTurn ) . toEqual ( {
1193+ kind : "text-slash" ,
1194+ source : "text" ,
1195+ authorized : true ,
1196+ body : "/status" ,
1197+ } ) ;
1198+ } ) ;
1199+
1200+ it ( "does not mark inline Feishu command tokens as text command turns" , async ( ) => {
1201+ mockShouldComputeCommandAuthorized . mockReturnValue ( true ) ;
1202+
1203+ const cfg : ClawdbotConfig = {
1204+ channels : {
1205+ feishu : {
1206+ dmPolicy : "open" ,
1207+ allowFrom : [ "ou-admin" ] ,
1208+ } ,
1209+ } ,
1210+ } as ClawdbotConfig ;
1211+
1212+ const event : FeishuMessageEvent = {
1213+ sender : {
1214+ sender_id : {
1215+ open_id : "ou-admin" ,
1216+ } ,
1217+ } ,
1218+ message : {
1219+ message_id : "msg-inline-command-token" ,
1220+ chat_id : "oc-dm" ,
1221+ chat_type : "p2p" ,
1222+ message_type : "text" ,
1223+ content : JSON . stringify ( { text : "please check /status" } ) ,
1224+ } ,
1225+ } ;
1226+
1227+ await dispatchMessage ( { cfg, event } ) ;
1228+
1229+ const context = mockCallArg < {
1230+ CommandAuthorized ?: boolean ;
1231+ CommandBody ?: string ;
1232+ CommandTurn ?: unknown ;
1233+ } > ( mockFinalizeInboundContext , 0 , 0 ) ;
1234+ expect ( context . CommandBody ) . toBe ( "please check /status" ) ;
1235+ expect ( context . CommandAuthorized ) . toBe ( true ) ;
1236+ expect ( context . CommandTurn ) . toEqual ( {
1237+ kind : "normal" ,
1238+ source : "message" ,
1239+ authorized : false ,
1240+ body : "please check /status" ,
1241+ } ) ;
1242+ } ) ;
1243+
11521244 it ( "does not enqueue inbound preview text as system events" , async ( ) => {
11531245 mockShouldComputeCommandAuthorized . mockReturnValue ( false ) ;
11541246
0 commit comments