digital-tools 2.1.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +2 -0
- package/dist/client.d.ts +109 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +21 -11
- package/dist/define.js.map +1 -1
- package/dist/function-ref.d.ts +229 -0
- package/dist/function-ref.d.ts.map +1 -0
- package/dist/function-ref.js +28 -0
- package/dist/function-ref.js.map +1 -0
- package/dist/function-sugar.d.ts +57 -0
- package/dist/function-sugar.d.ts.map +1 -0
- package/dist/function-sugar.js +79 -0
- package/dist/function-sugar.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
- package/dist/providers/analytics/mixpanel.js +21 -18
- package/dist/providers/analytics/mixpanel.js.map +1 -1
- package/dist/providers/calendar/cal-com.d.ts.map +1 -1
- package/dist/providers/calendar/cal-com.js +10 -10
- package/dist/providers/calendar/cal-com.js.map +1 -1
- package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
- package/dist/providers/calendar/google-calendar.js +4 -4
- package/dist/providers/calendar/google-calendar.js.map +1 -1
- package/dist/providers/crm/hubspot.d.ts.map +1 -1
- package/dist/providers/crm/hubspot.js +107 -85
- package/dist/providers/crm/hubspot.js.map +1 -1
- package/dist/providers/development/github.d.ts.map +1 -1
- package/dist/providers/development/github.js +40 -43
- package/dist/providers/development/github.js.map +1 -1
- package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
- package/dist/providers/ecommerce/shopify.js +79 -62
- package/dist/providers/ecommerce/shopify.js.map +1 -1
- package/dist/providers/email/resend.d.ts.map +1 -1
- package/dist/providers/email/resend.js +20 -16
- package/dist/providers/email/resend.js.map +1 -1
- package/dist/providers/email/sendgrid.d.ts.map +1 -1
- package/dist/providers/email/sendgrid.js +12 -9
- package/dist/providers/email/sendgrid.js.map +1 -1
- package/dist/providers/finance/stripe.d.ts.map +1 -1
- package/dist/providers/finance/stripe.js +44 -42
- package/dist/providers/finance/stripe.js.map +1 -1
- package/dist/providers/forms/typeform.d.ts.map +1 -1
- package/dist/providers/forms/typeform.js +68 -58
- package/dist/providers/forms/typeform.js.map +1 -1
- package/dist/providers/knowledge/notion.d.ts.map +1 -1
- package/dist/providers/knowledge/notion.js +75 -41
- package/dist/providers/knowledge/notion.js.map +1 -1
- package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
- package/dist/providers/marketing/mailchimp.js +74 -61
- package/dist/providers/marketing/mailchimp.js.map +1 -1
- package/dist/providers/media/cloudinary.d.ts.map +1 -1
- package/dist/providers/media/cloudinary.js +30 -28
- package/dist/providers/media/cloudinary.js.map +1 -1
- package/dist/providers/messaging/slack.d.ts.map +1 -1
- package/dist/providers/messaging/slack.js +75 -58
- package/dist/providers/messaging/slack.js.map +1 -1
- package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
- package/dist/providers/messaging/twilio-sms.js +33 -15
- package/dist/providers/messaging/twilio-sms.js.map +1 -1
- package/dist/providers/project-management/linear.d.ts.map +1 -1
- package/dist/providers/project-management/linear.js +31 -27
- package/dist/providers/project-management/linear.js.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.js +21 -18
- package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
- package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
- package/dist/providers/spreadsheet/xlsx.js +4 -4
- package/dist/providers/spreadsheet/xlsx.js.map +1 -1
- package/dist/providers/storage/index.js +1 -0
- package/dist/providers/storage/index.js.map +1 -1
- package/dist/providers/storage/s3.d.ts.map +1 -1
- package/dist/providers/storage/s3.js +36 -27
- package/dist/providers/storage/s3.js.map +1 -1
- package/dist/providers/support/zendesk.d.ts.map +1 -1
- package/dist/providers/support/zendesk.js +24 -25
- package/dist/providers/support/zendesk.js.map +1 -1
- package/dist/providers/tasks/todoist.d.ts.map +1 -1
- package/dist/providers/tasks/todoist.js +18 -18
- package/dist/providers/tasks/todoist.js.map +1 -1
- package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
- package/dist/providers/video-conferencing/google-meet.js +11 -11
- package/dist/providers/video-conferencing/google-meet.js.map +1 -1
- package/dist/providers/video-conferencing/jitsi.js +14 -14
- package/dist/providers/video-conferencing/jitsi.js.map +1 -1
- package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
- package/dist/providers/video-conferencing/teams.js +9 -7
- package/dist/providers/video-conferencing/teams.js.map +1 -1
- package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
- package/dist/providers/video-conferencing/zoom.js +26 -24
- package/dist/providers/video-conferencing/zoom.js.map +1 -1
- package/dist/tools/data.d.ts.map +1 -1
- package/dist/tools/data.js +5 -12
- package/dist/tools/data.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/system.d.ts +289 -0
- package/dist/tools/system.d.ts.map +1 -0
- package/dist/tools/system.js +752 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +22 -10
- package/dist/tools/web.js.map +1 -1
- package/dist/track-record.d.ts +101 -0
- package/dist/track-record.d.ts.map +1 -0
- package/dist/track-record.js +17 -0
- package/dist/track-record.js.map +1 -0
- package/dist/types.d.ts +210 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/verb-registration.d.ts +122 -0
- package/dist/verb-registration.d.ts.map +1 -0
- package/dist/verb-registration.js +176 -0
- package/dist/verb-registration.js.map +1 -0
- package/dist/worker.d.ts +93 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +315 -0
- package/dist/worker.js.map +1 -0
- package/dist/wrap.d.ts +89 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +225 -0
- package/dist/wrap.js.map +1 -0
- package/package.json +31 -14
- package/src/client.ts +136 -0
- package/src/define.ts +30 -24
- package/src/function-ref.ts +264 -0
- package/src/function-sugar.ts +134 -0
- package/src/index.ts +132 -10
- package/src/providers/analytics/mixpanel.ts +19 -18
- package/src/providers/calendar/cal-com.ts +29 -18
- package/src/providers/calendar/google-calendar.ts +20 -14
- package/src/providers/crm/hubspot.ts +225 -99
- package/src/providers/development/github.ts +206 -135
- package/src/providers/ecommerce/shopify.ts +250 -89
- package/src/providers/email/resend.ts +101 -28
- package/src/providers/email/sendgrid.ts +12 -9
- package/src/providers/finance/stripe.ts +128 -49
- package/src/providers/forms/typeform.ts +74 -58
- package/src/providers/knowledge/notion.ts +340 -88
- package/src/providers/marketing/mailchimp.ts +86 -70
- package/src/providers/media/cloudinary.ts +99 -41
- package/src/providers/messaging/slack.ts +283 -85
- package/src/providers/messaging/twilio-sms.ts +35 -15
- package/src/providers/project-management/linear.ts +143 -55
- package/src/providers/spreadsheet/google-sheets.ts +222 -56
- package/src/providers/spreadsheet/xlsx.ts +47 -16
- package/src/providers/storage/s3.ts +119 -47
- package/src/providers/support/zendesk.ts +196 -46
- package/src/providers/tasks/todoist.ts +20 -26
- package/src/providers/video-conferencing/google-meet.ts +17 -20
- package/src/providers/video-conferencing/jitsi.ts +14 -14
- package/src/providers/video-conferencing/teams.ts +14 -13
- package/src/providers/video-conferencing/zoom.ts +54 -49
- package/src/tools/data.ts +6 -16
- package/src/tools/index.ts +1 -0
- package/src/tools/system.ts +887 -0
- package/src/tools/web.ts +22 -10
- package/src/track-record.ts +106 -0
- package/src/types.ts +241 -13
- package/src/verb-registration.ts +197 -0
- package/src/worker.ts +370 -0
- package/src/wrap.ts +260 -0
- package/test/client.test.ts +146 -0
- package/test/communication-tools-extended.test.ts +734 -0
- package/test/data-tools-extended.test.ts +743 -0
- package/test/define-extended.test.ts +819 -0
- package/test/define.test.ts +150 -41
- package/test/entities.test.ts +623 -0
- package/test/extended-entities.test.ts +1228 -0
- package/test/provider-implementations.test.ts +725 -0
- package/test/provider-registry-extended.test.ts +583 -0
- package/test/providers/google-sheets.test.ts +851 -0
- package/test/providers/helpers.ts +554 -0
- package/test/providers/hubspot.test.ts +576 -0
- package/test/providers/slack.test.ts +932 -0
- package/test/providers/stripe.test.ts +701 -0
- package/test/providers.test.ts +578 -0
- package/test/system-tools-extended.test.ts +632 -0
- package/test/system.test.ts +673 -0
- package/test/tools.test.ts +15 -11
- package/test/types.test.ts +402 -0
- package/test/verb-registration.test.ts +395 -0
- package/test/web-tools.test.ts +553 -0
- package/test/worker-extended.test.ts +699 -0
- package/test/worker.test.ts +576 -0
- package/test/wrap.test.ts +366 -0
- package/tsconfig.json +3 -13
- package/vitest.config.ts +37 -0
- package/wrangler.jsonc +9 -0
- package/.turbo/turbo-build.log +0 -4
- package/LICENSE +0 -21
- package/dist/providers/voice/vapi.d.ts +0 -27
- package/dist/providers/voice/vapi.d.ts.map +0 -1
- package/dist/providers/voice/vapi.js +0 -440
- package/dist/providers/voice/vapi.js.map +0 -1
- package/src/define.js +0 -259
- package/src/entities/advertising.js +0 -999
- package/src/entities/ai.js +0 -756
- package/src/entities/analytics.js +0 -1588
- package/src/entities/automation.js +0 -601
- package/src/entities/communication.js +0 -1150
- package/src/entities/crm.js +0 -1386
- package/src/entities/design.js +0 -546
- package/src/entities/development.js +0 -2212
- package/src/entities/document.js +0 -874
- package/src/entities/ecommerce.js +0 -1429
- package/src/entities/experiment.js +0 -1039
- package/src/entities/finance.js +0 -3478
- package/src/entities/forms.js +0 -1892
- package/src/entities/hr.js +0 -661
- package/src/entities/identity.js +0 -997
- package/src/entities/index.js +0 -282
- package/src/entities/infrastructure.js +0 -1153
- package/src/entities/knowledge.js +0 -1438
- package/src/entities/marketing.js +0 -1610
- package/src/entities/media.js +0 -1634
- package/src/entities/notification.js +0 -1199
- package/src/entities/presentation.js +0 -1274
- package/src/entities/productivity.js +0 -1317
- package/src/entities/project-management.js +0 -1136
- package/src/entities/recruiting.js +0 -736
- package/src/entities/shipping.js +0 -509
- package/src/entities/signature.js +0 -1102
- package/src/entities/site.js +0 -222
- package/src/entities/spreadsheet.js +0 -1341
- package/src/entities/storage.js +0 -1198
- package/src/entities/support.js +0 -1166
- package/src/entities/video-conferencing.js +0 -1750
- package/src/entities/video.js +0 -950
- package/src/entities.js +0 -1663
- package/src/index.js +0 -74
- package/src/providers/analytics/index.js +0 -17
- package/src/providers/analytics/mixpanel.js +0 -255
- package/src/providers/calendar/cal-com.js +0 -303
- package/src/providers/calendar/google-calendar.js +0 -335
- package/src/providers/calendar/index.js +0 -20
- package/src/providers/crm/hubspot.js +0 -566
- package/src/providers/crm/index.js +0 -17
- package/src/providers/development/github.js +0 -472
- package/src/providers/development/index.js +0 -17
- package/src/providers/ecommerce/index.js +0 -17
- package/src/providers/ecommerce/shopify.js +0 -378
- package/src/providers/email/index.js +0 -20
- package/src/providers/email/resend.js +0 -258
- package/src/providers/email/sendgrid.js +0 -161
- package/src/providers/finance/index.js +0 -17
- package/src/providers/finance/stripe.js +0 -549
- package/src/providers/forms/index.js +0 -17
- package/src/providers/forms/typeform.js +0 -500
- package/src/providers/index.js +0 -123
- package/src/providers/knowledge/index.js +0 -17
- package/src/providers/knowledge/notion.js +0 -389
- package/src/providers/marketing/index.js +0 -17
- package/src/providers/marketing/mailchimp.js +0 -443
- package/src/providers/media/cloudinary.js +0 -318
- package/src/providers/media/index.js +0 -17
- package/src/providers/messaging/index.js +0 -20
- package/src/providers/messaging/slack.js +0 -393
- package/src/providers/messaging/twilio-sms.js +0 -249
- package/src/providers/project-management/index.js +0 -17
- package/src/providers/project-management/linear.js +0 -575
- package/src/providers/registry.js +0 -86
- package/src/providers/spreadsheet/google-sheets.js +0 -375
- package/src/providers/spreadsheet/index.js +0 -20
- package/src/providers/spreadsheet/xlsx.js +0 -423
- package/src/providers/storage/index.js +0 -24
- package/src/providers/storage/s3.js +0 -419
- package/src/providers/support/index.js +0 -17
- package/src/providers/support/zendesk.js +0 -373
- package/src/providers/tasks/index.js +0 -17
- package/src/providers/tasks/todoist.js +0 -286
- package/src/providers/types.js +0 -9
- package/src/providers/video-conferencing/google-meet.js +0 -286
- package/src/providers/video-conferencing/index.js +0 -31
- package/src/providers/video-conferencing/jitsi.js +0 -254
- package/src/providers/video-conferencing/teams.js +0 -270
- package/src/providers/video-conferencing/zoom.js +0 -332
- package/src/registry.js +0 -128
- package/src/tools/communication.js +0 -184
- package/src/tools/data.js +0 -205
- package/src/tools/index.js +0 -11
- package/src/tools/web.js +0 -137
- package/src/types.js +0 -10
- package/test/define.test.js +0 -306
- package/test/registry.test.js +0 -357
- package/test/tools.test.js +0 -363
|
@@ -29,6 +29,175 @@ import { defineProvider } from '../registry.js'
|
|
|
29
29
|
|
|
30
30
|
const SLACK_API_URL = 'https://slack.com/api'
|
|
31
31
|
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Slack API Response Types
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
/** Slack API response base */
|
|
37
|
+
interface SlackApiResponse {
|
|
38
|
+
ok: boolean
|
|
39
|
+
error?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Slack reaction from API */
|
|
43
|
+
interface SlackReaction {
|
|
44
|
+
name: string
|
|
45
|
+
count: number
|
|
46
|
+
users: string[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Slack message edit info */
|
|
50
|
+
interface SlackEditInfo {
|
|
51
|
+
ts?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Slack message from API */
|
|
55
|
+
interface SlackMessage {
|
|
56
|
+
ts: string
|
|
57
|
+
user: string
|
|
58
|
+
text: string
|
|
59
|
+
thread_ts?: string
|
|
60
|
+
reply_count?: number
|
|
61
|
+
reactions?: SlackReaction[]
|
|
62
|
+
edited?: SlackEditInfo
|
|
63
|
+
channel?: { id: string }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Slack channel topic/purpose from API */
|
|
67
|
+
interface SlackChannelMeta {
|
|
68
|
+
value?: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Slack channel from API */
|
|
72
|
+
interface SlackChannel {
|
|
73
|
+
id: string
|
|
74
|
+
name: string
|
|
75
|
+
topic?: SlackChannelMeta
|
|
76
|
+
purpose?: SlackChannelMeta
|
|
77
|
+
is_private?: boolean
|
|
78
|
+
is_archived?: boolean
|
|
79
|
+
num_members?: number
|
|
80
|
+
created: number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Slack user profile from API */
|
|
84
|
+
interface SlackUserProfile {
|
|
85
|
+
display_name?: string
|
|
86
|
+
email?: string
|
|
87
|
+
image_192?: string
|
|
88
|
+
title?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Slack user from API */
|
|
92
|
+
interface SlackUser {
|
|
93
|
+
id: string
|
|
94
|
+
name: string
|
|
95
|
+
real_name?: string
|
|
96
|
+
profile?: SlackUserProfile
|
|
97
|
+
is_admin?: boolean
|
|
98
|
+
is_owner?: boolean
|
|
99
|
+
is_bot?: boolean
|
|
100
|
+
deleted?: boolean
|
|
101
|
+
tz?: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Slack team icon from API */
|
|
105
|
+
interface SlackTeamIcon {
|
|
106
|
+
image_132?: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Slack team from API */
|
|
110
|
+
interface SlackTeam {
|
|
111
|
+
id: string
|
|
112
|
+
name: string
|
|
113
|
+
domain: string
|
|
114
|
+
icon?: SlackTeamIcon
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Slack response metadata */
|
|
118
|
+
interface SlackResponseMetadata {
|
|
119
|
+
next_cursor?: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Slack paging info */
|
|
123
|
+
interface SlackPaging {
|
|
124
|
+
pages: number
|
|
125
|
+
page: number
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Slack messages search result */
|
|
129
|
+
interface SlackMessagesSearchResult {
|
|
130
|
+
matches: SlackMessage[]
|
|
131
|
+
paging: SlackPaging
|
|
132
|
+
total: number
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Slack auth test response */
|
|
136
|
+
interface SlackAuthTestResponse extends SlackApiResponse {
|
|
137
|
+
user?: string
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Slack conversations open response */
|
|
141
|
+
interface SlackConversationsOpenResponse extends SlackApiResponse {
|
|
142
|
+
channel?: { id: string }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Slack post message response */
|
|
146
|
+
interface SlackPostMessageResponse extends SlackApiResponse {
|
|
147
|
+
ts?: string
|
|
148
|
+
channel?: string
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Slack conversations history response */
|
|
152
|
+
interface SlackConversationsHistoryResponse extends SlackApiResponse {
|
|
153
|
+
messages?: SlackMessage[]
|
|
154
|
+
has_more?: boolean
|
|
155
|
+
response_metadata?: SlackResponseMetadata
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Slack search messages response */
|
|
159
|
+
interface SlackSearchMessagesResponse extends SlackApiResponse {
|
|
160
|
+
messages: SlackMessagesSearchResult
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Slack conversations list response */
|
|
164
|
+
interface SlackConversationsListResponse extends SlackApiResponse {
|
|
165
|
+
channels: SlackChannel[]
|
|
166
|
+
response_metadata?: SlackResponseMetadata
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Slack conversations info response */
|
|
170
|
+
interface SlackConversationsInfoResponse extends SlackApiResponse {
|
|
171
|
+
channel: SlackChannel
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Slack conversations members response */
|
|
175
|
+
interface SlackConversationsMembersResponse extends SlackApiResponse {
|
|
176
|
+
members: string[]
|
|
177
|
+
response_metadata?: SlackResponseMetadata
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Slack users list response */
|
|
181
|
+
interface SlackUsersListResponse extends SlackApiResponse {
|
|
182
|
+
members: SlackUser[]
|
|
183
|
+
response_metadata?: SlackResponseMetadata
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Slack users info response */
|
|
187
|
+
interface SlackUsersInfoResponse extends SlackApiResponse {
|
|
188
|
+
user: SlackUser
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Slack presence response */
|
|
192
|
+
interface SlackPresenceResponse extends SlackApiResponse {
|
|
193
|
+
presence: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Slack team info response */
|
|
197
|
+
interface SlackTeamInfoResponse extends SlackApiResponse {
|
|
198
|
+
team: SlackTeam
|
|
199
|
+
}
|
|
200
|
+
|
|
32
201
|
/**
|
|
33
202
|
* Slack provider info
|
|
34
203
|
*/
|
|
@@ -49,17 +218,20 @@ export const slackInfo: ProviderInfo = {
|
|
|
49
218
|
export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
50
219
|
let token: string
|
|
51
220
|
|
|
52
|
-
async function slackApi
|
|
221
|
+
async function slackApi<T extends SlackApiResponse>(
|
|
222
|
+
method: string,
|
|
223
|
+
body?: Record<string, unknown>
|
|
224
|
+
): Promise<T> {
|
|
53
225
|
const response = await fetch(`${SLACK_API_URL}/${method}`, {
|
|
54
226
|
method: 'POST',
|
|
55
227
|
headers: {
|
|
56
228
|
'Content-Type': 'application/json; charset=utf-8',
|
|
57
229
|
Authorization: `Bearer ${token}`,
|
|
58
230
|
},
|
|
59
|
-
body
|
|
231
|
+
...(body !== undefined && { body: JSON.stringify(body) }),
|
|
60
232
|
})
|
|
61
233
|
|
|
62
|
-
const data = await response.json()
|
|
234
|
+
const data = (await response.json()) as T
|
|
63
235
|
return data
|
|
64
236
|
}
|
|
65
237
|
|
|
@@ -67,7 +239,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
67
239
|
info: slackInfo,
|
|
68
240
|
|
|
69
241
|
async initialize(cfg: ProviderConfig): Promise<void> {
|
|
70
|
-
token = (cfg
|
|
242
|
+
token = (cfg['accessToken'] || cfg['botToken']) as string
|
|
71
243
|
if (!token) {
|
|
72
244
|
throw new Error('Slack access token or bot token is required')
|
|
73
245
|
}
|
|
@@ -76,11 +248,11 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
76
248
|
async healthCheck(): Promise<ProviderHealth> {
|
|
77
249
|
const start = Date.now()
|
|
78
250
|
try {
|
|
79
|
-
const data = await slackApi('auth.test')
|
|
251
|
+
const data = await slackApi<SlackAuthTestResponse>('auth.test')
|
|
80
252
|
return {
|
|
81
253
|
healthy: data.ok === true,
|
|
82
254
|
latencyMs: Date.now() - start,
|
|
83
|
-
message: data.ok ? `Connected as ${data.user}` : data.error,
|
|
255
|
+
message: data.ok ? `Connected as ${data.user}` : data.error || 'Unknown error',
|
|
84
256
|
checkedAt: new Date(),
|
|
85
257
|
}
|
|
86
258
|
} catch (error) {
|
|
@@ -103,17 +275,19 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
103
275
|
}
|
|
104
276
|
|
|
105
277
|
if (options.channel) {
|
|
106
|
-
body
|
|
278
|
+
body['channel'] = options.channel
|
|
107
279
|
} else if (options.userId) {
|
|
108
280
|
// Open DM conversation first
|
|
109
|
-
const dm = await slackApi('conversations.open', {
|
|
281
|
+
const dm = await slackApi<SlackConversationsOpenResponse>('conversations.open', {
|
|
282
|
+
users: options.userId,
|
|
283
|
+
})
|
|
110
284
|
if (!dm.ok) {
|
|
111
285
|
return {
|
|
112
286
|
success: false,
|
|
113
|
-
error: { code: dm.error, message: `Failed to open DM: ${dm.error}` },
|
|
287
|
+
error: { code: dm.error || 'UNKNOWN', message: `Failed to open DM: ${dm.error}` },
|
|
114
288
|
}
|
|
115
289
|
}
|
|
116
|
-
body
|
|
290
|
+
body['channel'] = dm.channel?.id
|
|
117
291
|
} else {
|
|
118
292
|
return {
|
|
119
293
|
success: false,
|
|
@@ -122,34 +296,34 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
122
296
|
}
|
|
123
297
|
|
|
124
298
|
if (options.threadId) {
|
|
125
|
-
body
|
|
299
|
+
body['thread_ts'] = options.threadId
|
|
126
300
|
}
|
|
127
301
|
|
|
128
302
|
if (options.blocks) {
|
|
129
|
-
body
|
|
303
|
+
body['blocks'] = options.blocks
|
|
130
304
|
}
|
|
131
305
|
|
|
132
306
|
if (options.metadata) {
|
|
133
|
-
body
|
|
307
|
+
body['metadata'] = {
|
|
134
308
|
event_type: 'message_metadata',
|
|
135
309
|
event_payload: options.metadata,
|
|
136
310
|
}
|
|
137
311
|
}
|
|
138
312
|
|
|
139
|
-
const data = await slackApi('chat.postMessage', body)
|
|
313
|
+
const data = await slackApi<SlackPostMessageResponse>('chat.postMessage', body)
|
|
140
314
|
|
|
141
315
|
if (data.ok) {
|
|
142
316
|
return {
|
|
143
317
|
success: true,
|
|
144
|
-
messageId: data.ts,
|
|
145
|
-
timestamp: data.ts,
|
|
146
|
-
channel: data.channel,
|
|
318
|
+
...(data.ts !== undefined && { messageId: data.ts }),
|
|
319
|
+
...(data.ts !== undefined && { timestamp: data.ts }),
|
|
320
|
+
...(data.channel !== undefined && { channel: data.channel }),
|
|
147
321
|
}
|
|
148
322
|
}
|
|
149
323
|
|
|
150
324
|
return {
|
|
151
325
|
success: false,
|
|
152
|
-
error: { code: data.error, message: data.error },
|
|
326
|
+
error: { code: data.error || 'UNKNOWN', message: data.error || 'Unknown error' },
|
|
153
327
|
}
|
|
154
328
|
},
|
|
155
329
|
|
|
@@ -160,33 +334,33 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
160
334
|
}
|
|
161
335
|
|
|
162
336
|
if (blocks) {
|
|
163
|
-
body
|
|
337
|
+
body['blocks'] = blocks
|
|
164
338
|
}
|
|
165
339
|
|
|
166
|
-
const data = await slackApi('chat.update', body)
|
|
340
|
+
const data = await slackApi<SlackPostMessageResponse>('chat.update', body)
|
|
167
341
|
|
|
168
342
|
if (data.ok) {
|
|
169
343
|
return {
|
|
170
344
|
success: true,
|
|
171
|
-
messageId: data.ts,
|
|
172
|
-
timestamp: data.ts,
|
|
173
|
-
channel: data.channel,
|
|
345
|
+
...(data.ts !== undefined && { messageId: data.ts }),
|
|
346
|
+
...(data.ts !== undefined && { timestamp: data.ts }),
|
|
347
|
+
...(data.channel !== undefined && { channel: data.channel }),
|
|
174
348
|
}
|
|
175
349
|
}
|
|
176
350
|
|
|
177
351
|
return {
|
|
178
352
|
success: false,
|
|
179
|
-
error: { code: data.error, message: data.error },
|
|
353
|
+
error: { code: data.error || 'UNKNOWN', message: data.error || 'Unknown error' },
|
|
180
354
|
}
|
|
181
355
|
},
|
|
182
356
|
|
|
183
357
|
async delete(messageId: string, channel: string): Promise<boolean> {
|
|
184
|
-
const data = await slackApi('chat.delete', { ts: messageId, channel })
|
|
358
|
+
const data = await slackApi<SlackApiResponse>('chat.delete', { ts: messageId, channel })
|
|
185
359
|
return data.ok === true
|
|
186
360
|
},
|
|
187
361
|
|
|
188
362
|
async react(messageId: string, channel: string, emoji: string): Promise<boolean> {
|
|
189
|
-
const data = await slackApi('reactions.add', {
|
|
363
|
+
const data = await slackApi<SlackApiResponse>('reactions.add', {
|
|
190
364
|
name: emoji.replace(/:/g, ''),
|
|
191
365
|
timestamp: messageId,
|
|
192
366
|
channel,
|
|
@@ -195,7 +369,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
195
369
|
},
|
|
196
370
|
|
|
197
371
|
async unreact(messageId: string, channel: string, emoji: string): Promise<boolean> {
|
|
198
|
-
const data = await slackApi('reactions.remove', {
|
|
372
|
+
const data = await slackApi<SlackApiResponse>('reactions.remove', {
|
|
199
373
|
name: emoji.replace(/:/g, ''),
|
|
200
374
|
timestamp: messageId,
|
|
201
375
|
channel,
|
|
@@ -204,7 +378,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
204
378
|
},
|
|
205
379
|
|
|
206
380
|
async getMessage(messageId: string, channel: string): Promise<MessageData | null> {
|
|
207
|
-
const data = await slackApi('conversations.history', {
|
|
381
|
+
const data = await slackApi<SlackConversationsHistoryResponse>('conversations.history', {
|
|
208
382
|
channel,
|
|
209
383
|
latest: messageId,
|
|
210
384
|
inclusive: true,
|
|
@@ -216,40 +390,49 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
216
390
|
}
|
|
217
391
|
|
|
218
392
|
const msg = data.messages[0]
|
|
393
|
+
if (!msg) return null
|
|
219
394
|
return mapSlackMessage(msg, channel)
|
|
220
395
|
},
|
|
221
396
|
|
|
222
|
-
async listMessages(
|
|
397
|
+
async listMessages(
|
|
398
|
+
channel: string,
|
|
399
|
+
options?: MessageListOptions
|
|
400
|
+
): Promise<PaginatedResult<MessageData>> {
|
|
223
401
|
const body: Record<string, unknown> = {
|
|
224
402
|
channel,
|
|
225
403
|
limit: options?.limit || 100,
|
|
226
404
|
}
|
|
227
405
|
|
|
228
406
|
if (options?.cursor) {
|
|
229
|
-
body
|
|
407
|
+
body['cursor'] = options.cursor
|
|
230
408
|
}
|
|
231
409
|
if (options?.since) {
|
|
232
|
-
body
|
|
410
|
+
body['oldest'] = (options.since.getTime() / 1000).toString()
|
|
233
411
|
}
|
|
234
412
|
if (options?.until) {
|
|
235
|
-
body
|
|
413
|
+
body['latest'] = (options.until.getTime() / 1000).toString()
|
|
236
414
|
}
|
|
237
415
|
|
|
238
|
-
const data = await slackApi('conversations.history', body)
|
|
416
|
+
const data = await slackApi<SlackConversationsHistoryResponse>('conversations.history', body)
|
|
239
417
|
|
|
240
418
|
if (!data.ok) {
|
|
241
419
|
return { items: [], hasMore: false }
|
|
242
420
|
}
|
|
243
421
|
|
|
244
422
|
return {
|
|
245
|
-
items: data.messages.map((msg
|
|
423
|
+
items: (data.messages || []).map((msg) => mapSlackMessage(msg, channel)),
|
|
246
424
|
hasMore: data.has_more || false,
|
|
247
|
-
|
|
425
|
+
...(data.response_metadata?.next_cursor !== undefined && {
|
|
426
|
+
nextCursor: data.response_metadata.next_cursor,
|
|
427
|
+
}),
|
|
248
428
|
}
|
|
249
429
|
},
|
|
250
430
|
|
|
251
|
-
async searchMessages(
|
|
252
|
-
|
|
431
|
+
async searchMessages(
|
|
432
|
+
query: string,
|
|
433
|
+
options?: MessageSearchOptions
|
|
434
|
+
): Promise<PaginatedResult<MessageData>> {
|
|
435
|
+
const data = await slackApi<SlackSearchMessagesResponse>('search.messages', {
|
|
253
436
|
query,
|
|
254
437
|
count: options?.limit || 100,
|
|
255
438
|
page: options?.offset ? Math.floor(options.offset / (options.limit || 100)) + 1 : 1,
|
|
@@ -260,7 +443,9 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
260
443
|
}
|
|
261
444
|
|
|
262
445
|
return {
|
|
263
|
-
items: data.messages.matches.map((match
|
|
446
|
+
items: data.messages.matches.map((match) =>
|
|
447
|
+
mapSlackMessage(match, match.channel?.id || '')
|
|
448
|
+
),
|
|
264
449
|
hasMore: data.messages.paging.pages > data.messages.paging.page,
|
|
265
450
|
total: data.messages.total,
|
|
266
451
|
}
|
|
@@ -273,14 +458,16 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
273
458
|
}
|
|
274
459
|
|
|
275
460
|
if (options?.cursor) {
|
|
276
|
-
body
|
|
461
|
+
body['cursor'] = options.cursor
|
|
277
462
|
}
|
|
278
463
|
|
|
279
464
|
if (options?.types) {
|
|
280
|
-
body
|
|
465
|
+
body['types'] = options.types
|
|
466
|
+
.map((t) => (t === 'private' ? 'private_channel' : 'public_channel'))
|
|
467
|
+
.join(',')
|
|
281
468
|
}
|
|
282
469
|
|
|
283
|
-
const data = await slackApi('conversations.list', body)
|
|
470
|
+
const data = await slackApi<SlackConversationsListResponse>('conversations.list', body)
|
|
284
471
|
|
|
285
472
|
if (!data.ok) {
|
|
286
473
|
return { items: [], hasMore: false }
|
|
@@ -289,12 +476,16 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
289
476
|
return {
|
|
290
477
|
items: data.channels.map(mapSlackChannel),
|
|
291
478
|
hasMore: data.response_metadata?.next_cursor ? true : false,
|
|
292
|
-
|
|
479
|
+
...(data.response_metadata?.next_cursor !== undefined && {
|
|
480
|
+
nextCursor: data.response_metadata.next_cursor,
|
|
481
|
+
}),
|
|
293
482
|
}
|
|
294
483
|
},
|
|
295
484
|
|
|
296
485
|
async getChannel(channelId: string): Promise<ChannelData | null> {
|
|
297
|
-
const data = await slackApi('conversations.info', {
|
|
486
|
+
const data = await slackApi<SlackConversationsInfoResponse>('conversations.info', {
|
|
487
|
+
channel: channelId,
|
|
488
|
+
})
|
|
298
489
|
|
|
299
490
|
if (!data.ok) {
|
|
300
491
|
return null
|
|
@@ -309,7 +500,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
309
500
|
is_private: options?.isPrivate || false,
|
|
310
501
|
}
|
|
311
502
|
|
|
312
|
-
const data = await slackApi('conversations.create', body)
|
|
503
|
+
const data = await slackApi<SlackConversationsInfoResponse>('conversations.create', body)
|
|
313
504
|
|
|
314
505
|
if (!data.ok) {
|
|
315
506
|
throw new Error(`Failed to create channel: ${data.error}`)
|
|
@@ -319,7 +510,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
319
510
|
|
|
320
511
|
// Set topic if provided
|
|
321
512
|
if (options?.topic) {
|
|
322
|
-
await slackApi('conversations.setTopic', {
|
|
513
|
+
await slackApi<SlackApiResponse>('conversations.setTopic', {
|
|
323
514
|
channel: data.channel.id,
|
|
324
515
|
topic: options.topic,
|
|
325
516
|
})
|
|
@@ -327,7 +518,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
327
518
|
|
|
328
519
|
// Set description/purpose if provided
|
|
329
520
|
if (options?.description) {
|
|
330
|
-
await slackApi('conversations.setPurpose', {
|
|
521
|
+
await slackApi<SlackApiResponse>('conversations.setPurpose', {
|
|
331
522
|
channel: data.channel.id,
|
|
332
523
|
purpose: options.description,
|
|
333
524
|
})
|
|
@@ -337,17 +528,17 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
337
528
|
},
|
|
338
529
|
|
|
339
530
|
async archiveChannel(channelId: string): Promise<boolean> {
|
|
340
|
-
const data = await slackApi('conversations.archive', { channel: channelId })
|
|
531
|
+
const data = await slackApi<SlackApiResponse>('conversations.archive', { channel: channelId })
|
|
341
532
|
return data.ok === true
|
|
342
533
|
},
|
|
343
534
|
|
|
344
535
|
async joinChannel(channelId: string): Promise<boolean> {
|
|
345
|
-
const data = await slackApi('conversations.join', { channel: channelId })
|
|
536
|
+
const data = await slackApi<SlackApiResponse>('conversations.join', { channel: channelId })
|
|
346
537
|
return data.ok === true
|
|
347
538
|
},
|
|
348
539
|
|
|
349
540
|
async leaveChannel(channelId: string): Promise<boolean> {
|
|
350
|
-
const data = await slackApi('conversations.leave', { channel: channelId })
|
|
541
|
+
const data = await slackApi<SlackApiResponse>('conversations.leave', { channel: channelId })
|
|
351
542
|
return data.ok === true
|
|
352
543
|
},
|
|
353
544
|
|
|
@@ -357,48 +548,53 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
357
548
|
}
|
|
358
549
|
|
|
359
550
|
if (options?.cursor) {
|
|
360
|
-
body
|
|
551
|
+
body['cursor'] = options.cursor
|
|
361
552
|
}
|
|
362
553
|
|
|
363
|
-
let data: any
|
|
364
|
-
|
|
365
554
|
if (options?.channel) {
|
|
366
555
|
// Get members of specific channel
|
|
367
|
-
|
|
368
|
-
|
|
556
|
+
const channelData = await slackApi<SlackConversationsMembersResponse>(
|
|
557
|
+
'conversations.members',
|
|
558
|
+
{ ...body, channel: options.channel }
|
|
559
|
+
)
|
|
560
|
+
if (!channelData.ok) {
|
|
369
561
|
return { items: [], hasMore: false }
|
|
370
562
|
}
|
|
371
563
|
|
|
372
564
|
// Fetch user info for each member
|
|
373
565
|
const members = await Promise.all(
|
|
374
|
-
|
|
375
|
-
const userInfo = await slackApi('users.info', { user: userId })
|
|
566
|
+
channelData.members.map(async (userId: string) => {
|
|
567
|
+
const userInfo = await slackApi<SlackUsersInfoResponse>('users.info', { user: userId })
|
|
376
568
|
return userInfo.ok ? mapSlackUser(userInfo.user) : null
|
|
377
569
|
})
|
|
378
570
|
)
|
|
379
571
|
|
|
380
572
|
return {
|
|
381
|
-
items: members.filter(
|
|
382
|
-
hasMore:
|
|
383
|
-
|
|
573
|
+
items: members.filter((m): m is MemberData => m !== null),
|
|
574
|
+
hasMore: channelData.response_metadata?.next_cursor ? true : false,
|
|
575
|
+
...(channelData.response_metadata?.next_cursor !== undefined && {
|
|
576
|
+
nextCursor: channelData.response_metadata.next_cursor,
|
|
577
|
+
}),
|
|
384
578
|
}
|
|
385
579
|
} else {
|
|
386
580
|
// Get all workspace members
|
|
387
|
-
|
|
388
|
-
if (!
|
|
581
|
+
const usersData = await slackApi<SlackUsersListResponse>('users.list', body)
|
|
582
|
+
if (!usersData.ok) {
|
|
389
583
|
return { items: [], hasMore: false }
|
|
390
584
|
}
|
|
391
585
|
|
|
392
586
|
return {
|
|
393
|
-
items:
|
|
394
|
-
hasMore:
|
|
395
|
-
|
|
587
|
+
items: usersData.members.filter((m) => !m.deleted).map(mapSlackUser),
|
|
588
|
+
hasMore: usersData.response_metadata?.next_cursor ? true : false,
|
|
589
|
+
...(usersData.response_metadata?.next_cursor !== undefined && {
|
|
590
|
+
nextCursor: usersData.response_metadata.next_cursor,
|
|
591
|
+
}),
|
|
396
592
|
}
|
|
397
593
|
}
|
|
398
594
|
},
|
|
399
595
|
|
|
400
596
|
async getMember(userId: string): Promise<MemberData | null> {
|
|
401
|
-
const data = await slackApi('users.info', { user: userId })
|
|
597
|
+
const data = await slackApi<SlackUsersInfoResponse>('users.info', { user: userId })
|
|
402
598
|
|
|
403
599
|
if (!data.ok) {
|
|
404
600
|
return null
|
|
@@ -408,7 +604,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
408
604
|
},
|
|
409
605
|
|
|
410
606
|
async getPresence(userId: string): Promise<PresenceData> {
|
|
411
|
-
const data = await slackApi('users.getPresence', { user: userId })
|
|
607
|
+
const data = await slackApi<SlackPresenceResponse>('users.getPresence', { user: userId })
|
|
412
608
|
|
|
413
609
|
return {
|
|
414
610
|
userId,
|
|
@@ -417,7 +613,7 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
417
613
|
},
|
|
418
614
|
|
|
419
615
|
async getWorkspace(): Promise<WorkspaceData> {
|
|
420
|
-
const data = await slackApi('team.info')
|
|
616
|
+
const data = await slackApi<SlackTeamInfoResponse>('team.info')
|
|
421
617
|
|
|
422
618
|
if (!data.ok) {
|
|
423
619
|
throw new Error(`Failed to get workspace info: ${data.error}`)
|
|
@@ -427,37 +623,39 @@ export function createSlackProvider(config: ProviderConfig): MessagingProvider {
|
|
|
427
623
|
id: data.team.id,
|
|
428
624
|
name: data.team.name,
|
|
429
625
|
domain: data.team.domain,
|
|
430
|
-
icon: data.team.icon
|
|
626
|
+
...(data.team.icon?.image_132 !== undefined && { icon: data.team.icon.image_132 }),
|
|
431
627
|
}
|
|
432
628
|
},
|
|
433
629
|
}
|
|
434
630
|
}
|
|
435
631
|
|
|
436
|
-
function mapSlackMessage(msg:
|
|
632
|
+
function mapSlackMessage(msg: SlackMessage, channel: string): MessageData {
|
|
437
633
|
return {
|
|
438
634
|
id: msg.ts,
|
|
439
635
|
channel,
|
|
440
636
|
userId: msg.user,
|
|
441
637
|
text: msg.text,
|
|
442
638
|
timestamp: msg.ts,
|
|
443
|
-
threadId: msg.thread_ts,
|
|
444
|
-
replyCount: msg.reply_count,
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
639
|
+
...(msg.thread_ts !== undefined && { threadId: msg.thread_ts }),
|
|
640
|
+
...(msg.reply_count !== undefined && { replyCount: msg.reply_count }),
|
|
641
|
+
...(msg.reactions && {
|
|
642
|
+
reactions: msg.reactions.map((r) => ({
|
|
643
|
+
emoji: r.name,
|
|
644
|
+
count: r.count,
|
|
645
|
+
users: r.users,
|
|
646
|
+
})),
|
|
647
|
+
}),
|
|
450
648
|
edited: !!msg.edited,
|
|
451
|
-
|
|
649
|
+
...(msg.edited?.ts && { editedAt: new Date(parseFloat(msg.edited.ts) * 1000) }),
|
|
452
650
|
}
|
|
453
651
|
}
|
|
454
652
|
|
|
455
|
-
function mapSlackChannel(ch:
|
|
653
|
+
function mapSlackChannel(ch: SlackChannel): ChannelData {
|
|
456
654
|
return {
|
|
457
655
|
id: ch.id,
|
|
458
656
|
name: ch.name,
|
|
459
|
-
topic: ch.topic
|
|
460
|
-
description: ch.purpose
|
|
657
|
+
...(ch.topic?.value !== undefined && { topic: ch.topic.value }),
|
|
658
|
+
...(ch.purpose?.value !== undefined && { description: ch.purpose.value }),
|
|
461
659
|
isPrivate: ch.is_private || false,
|
|
462
660
|
isArchived: ch.is_archived || false,
|
|
463
661
|
memberCount: ch.num_members || 0,
|
|
@@ -465,17 +663,17 @@ function mapSlackChannel(ch: any): ChannelData {
|
|
|
465
663
|
}
|
|
466
664
|
}
|
|
467
665
|
|
|
468
|
-
function mapSlackUser(user:
|
|
666
|
+
function mapSlackUser(user: SlackUser): MemberData {
|
|
469
667
|
return {
|
|
470
668
|
id: user.id,
|
|
471
669
|
username: user.name,
|
|
472
670
|
displayName: user.real_name || user.profile?.display_name || user.name,
|
|
473
|
-
email: user.profile
|
|
474
|
-
avatar: user.profile
|
|
475
|
-
title: user.profile
|
|
671
|
+
...(user.profile?.email !== undefined && { email: user.profile.email }),
|
|
672
|
+
...(user.profile?.image_192 !== undefined && { avatar: user.profile.image_192 }),
|
|
673
|
+
...(user.profile?.title !== undefined && { title: user.profile.title }),
|
|
476
674
|
isAdmin: user.is_admin || user.is_owner || false,
|
|
477
675
|
isBot: user.is_bot || false,
|
|
478
|
-
timezone: user.tz,
|
|
676
|
+
...(user.tz !== undefined && { timezone: user.tz }),
|
|
479
677
|
}
|
|
480
678
|
}
|
|
481
679
|
|