@@ -311,22 +311,7 @@ const handlers = {
311311 . where ( 'revoked_at' , 'is' , null )
312312 . executeTakeFirst ( )
313313 if ( accessKey ) {
314- await event . channel . postEphemeral (
315- event . user ,
316- {
317- card : chat . Card ( {
318- children : [
319- chat . CardText (
320- 'Send and receive payments in this workspace.\nUse `/tip disconnect` to disconnect.' ,
321- ) ,
322- ] ,
323- title : 'Already connected to Tipbot' ,
324- } ) ,
325- fallbackText :
326- 'Already connected to Tipbot\nSend and receive payments in this workspace.\nUse `/tip disconnect` to disconnect.' ,
327- } ,
328- { fallbackToDM : false } ,
329- )
314+ await event . channel . postEphemeral ( event . user , 'Already connected' , { fallbackToDM : false } )
330315 return
331316 }
332317 }
@@ -335,13 +320,10 @@ const handlers = {
335320 const token = Nanoid . generate ( )
336321 const accessKey = AccessKey . generate ( )
337322 const linkAction = member . account_id ? 'Reconnect' : 'Connect'
338- const linkButtonLabel = member . account_id ? 'Refresh connection' : 'Connect Tipbot'
339- const linkDescription = member . account_id
340- ? 'Tipbot connection needs a quick refresh before sending payments.'
341- : 'Connect once to send and receive payments in Slack.'
342- const linkTitle = member . account_id ? 'Refresh Tipbot connection' : 'Connect to Tipbot'
323+ const linkButtonLabel = member . account_id ? 'Refresh connection' : 'Connect to Tipbot'
324+ const linkDescription = 'Link expires in 10 minutes.'
343325 const linkUrl = `https://${ env . HOST } /connect/${ token } `
344- const linkText = `${ linkTitle } \n ${ linkDescription } \nThis private link expires in 10 minutes.\n ${ linkAction } to Tipbot: ${ linkUrl } `
326+ const linkText = `${ linkAction } to Tipbot: ${ linkUrl } \n ${ linkDescription } `
345327 const linkExpiresAt = new Date ( now . getTime ( ) + 10 * 60 * 1000 ) . toISOString ( ) // 10 minutes
346328 const accessKeyExpiresAt = new Date ( now . getTime ( ) + 30 * 24 * 60 * 60 * 1000 ) . toISOString ( ) // 30 days
347329 await ctx . db
@@ -373,14 +355,13 @@ const handlers = {
373355 {
374356 card : chat . Card ( {
375357 children : [
376- chat . CardText ( `${ linkDescription } \nThis private link expires in 10 minutes.` ) ,
377- chat . CardLink ( { label : 'Private link' , url : linkUrl } ) ,
358+ chat . CardLink ( { label : `${ linkAction } to Tipbot` , url : linkUrl } ) ,
378359 chat . Actions ( [
379360 chat . LinkButton ( { label : linkButtonLabel , style : 'primary' , url : linkUrl } ) ,
380361 chat . Button ( { id : 'connect_cancel' , label : 'Cancel' } ) ,
381362 ] ) ,
363+ chat . CardText ( linkDescription , { style : 'muted' } ) ,
382364 ] ,
383- title : linkTitle ,
384365 } ) ,
385366 fallbackText : linkText ,
386367 } ,
@@ -426,31 +407,52 @@ const handlers = {
426407 fallbackToDM : false ,
427408 } )
428409 } ,
429- async help ( event , _ctx ) {
430- await event . channel . postEphemeral (
431- event . user ,
432- {
433- card : chat . Card ( {
434- children : [
435- chat . Table ( {
436- headers : [ 'Command' , 'Description' ] ,
437- rows : [
438- [ '`/tip @account for coffee`' , 'Send a payment in chat' ] ,
439- [ '`/tip config`' , 'View workspace settings' ] ,
440- [ '`/tip connect`' , 'Connect Tipbot' ] ,
441- [ '`/tip disconnect`' , 'Disconnect Tipbot' ] ,
442- [ '`/tip help`' , 'Show commands' ] ,
443- [ '`/tip status`' , 'Check Tipbot connection' ] ,
444- ] ,
445- } ) ,
410+ async help ( event , ctx ) {
411+ if ( ctx . provider . type !== 'slack' ) throw new Error ( 'Provider is not implemented yet.' )
412+
413+ const installation = await getSlack ( ) . getInstallation ( ctx . provider . id )
414+ if ( ! installation ) return
415+
416+ const rows = [
417+ [ '/tip @account for coffee' , 'Send a payment in chat' ] ,
418+ [ '/tip config' , 'View workspace settings' ] ,
419+ [ '/tip connect' , 'Connect Tipbot' ] ,
420+ [ '/tip disconnect' , 'Disconnect Tipbot' ] ,
421+ [ '/tip help' , 'Show commands' ] ,
422+ [ '/tip status' , 'Check Tipbot connection' ] ,
423+ ]
424+ const body = new URLSearchParams ( )
425+ body . set ( 'channel' , event . channel . id . replace ( / ^ s l a c k : / , '' ) )
426+ body . set ( 'text' , `Tipbot commands\n${ rows . map ( ( row ) => `${ row [ 0 ] } ${ row [ 1 ] } ` ) . join ( '\n' ) } ` )
427+ body . set (
428+ 'blocks' ,
429+ JSON . stringify ( [
430+ { text : { emoji : true , text : 'Tipbot commands' , type : 'plain_text' } , type : 'header' } ,
431+ {
432+ rows : [
433+ [ slackTableCell ( 'Command' ) , slackTableCell ( 'Description' ) ] ,
434+ ...rows . map ( ( row ) => [ slackTableCell ( row [ 0 ] , { code : true } ) , slackTableCell ( row [ 1 ] ) ] ) ,
446435 ] ,
447- title : 'Tipbot commands' ,
448- } ) ,
449- fallbackText :
450- 'Tipbot commands\n`/tip @account for coffee` Send a payment in chat\n`/tip config` View workspace settings\n`/tip connect` Connect Tipbot\n`/tip disconnect` Disconnect Tipbot\n`/tip help` Show commands\n`/tip status` Check Tipbot connection' ,
451- } ,
452- { fallbackToDM : false } ,
436+ type : 'table' ,
437+ } ,
438+ ] ) ,
439+ )
440+ body . set ( 'user' , event . user . userId )
441+ const response = await getSlack ( ) . withBotToken ( installation . botToken , ( ) =>
442+ fetch ( `${ env . SLACK_API_URL } /chat.postEphemeral` , {
443+ body,
444+ headers : { authorization : `Bearer ${ installation . botToken } ` } ,
445+ method : 'POST' ,
446+ } ) ,
453447 )
448+ const json = z . parse (
449+ z . object ( {
450+ error : z . string ( ) . optional ( ) ,
451+ ok : z . boolean ( ) . optional ( ) ,
452+ } ) ,
453+ await response . json ( ) ,
454+ )
455+ if ( ! json . ok ) throw new Error ( json . error ?? 'Slack API chat.postEphemeral failed.' )
454456 } ,
455457 async status ( event , ctx ) {
456458 const workspace = await ctx . db
@@ -488,7 +490,22 @@ const handlers = {
488490
489491 await event . channel . postEphemeral (
490492 event . user ,
491- `Account ID: ${ member . account_id } \nAddress: ${ member . account_address } \nProvider user ID: ${ member . provider_user_id } ` ,
493+ {
494+ card : chat . Card ( {
495+ children : [
496+ chat . Table ( {
497+ headers : [ 'Field' , 'Value' ] ,
498+ rows : [
499+ [ 'Account ID' , member . account_id ] ,
500+ [ 'Address' , member . account_address ] ,
501+ [ 'Provider user ID' , member . provider_user_id ] ,
502+ ] ,
503+ } ) ,
504+ ] ,
505+ title : 'Status' ,
506+ } ) ,
507+ fallbackText : `Status\nAccount ID ${ member . account_id } \nAddress ${ member . account_address } \nProvider user ID ${ member . provider_user_id } ` ,
508+ } ,
492509 { fallbackToDM : false } ,
493510 )
494511 } ,
@@ -663,14 +680,14 @@ function configCard(
663680 return {
664681 card : chat . Card ( {
665682 children : [
666- chat . Fields ( [
667- chat . Field ( { label : 'Network ', value : networkLabel } ) ,
668- chat . Field ( {
669- label : 'Default token' ,
670- value : `< ${ Tempo . formatTokenLink ( workspace . chain_id , tokenAddress ) } | ${ token . symbol } >` ,
671- } ) ,
672- chat . Field ( { label : 'Default amount' , value : formatAmount ( workspace . default_amount ) } ) ,
673- ] ) ,
683+ chat . Table ( {
684+ headers : [ 'Setting ', 'Value' ] ,
685+ rows : [
686+ [ 'Network' , networkLabel ] ,
687+ [ 'Default token' , token . symbol ] ,
688+ [ 'Default amount' , formatAmount ( workspace . default_amount ) ] ,
689+ ] ,
690+ } ) ,
674691 ...( options ?. canEdit
675692 ? [
676693 chat . Actions ( [
@@ -689,3 +706,15 @@ function configCard(
689706 fallbackText : `${ options ?. title ?? 'Workspace settings' } \nNetwork ${ networkLabel } \nDefault token ${ token . symbol } ${ Tempo . formatTokenLink ( workspace . chain_id , tokenAddress ) } \nDefault amount ${ formatAmount ( workspace . default_amount ) } ` ,
690707 }
691708}
709+
710+ function slackTableCell ( text : string , style ?: { code ?: boolean } ) {
711+ return {
712+ elements : [
713+ {
714+ elements : [ style ? { style, text, type : 'text' } : { text, type : 'text' } ] ,
715+ type : 'rich_text_section' ,
716+ } ,
717+ ] ,
718+ type : 'rich_text' ,
719+ }
720+ }
0 commit comments