Skip to content

Commit c02d2d1

Browse files
feat(tiktok): add Content Posting API support- Add video.publish scope to OAuth configuration- Add Query Creator Info tool to check posting permissions- Add Direct Post Video tool to publish videos from URL- Add Get Post Status tool to track post progress- Update TikTok block with new operations and UI fields- Add type definitions for all new operations
1 parent 501f711 commit c02d2d1

File tree

11 files changed

+630
-32
lines changed

11 files changed

+630
-32
lines changed

apps/sim/blocks/blocks/tiktok.ts

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import type { TikTokResponse } from '@/tools/tiktok/types'
66
export const TikTokBlock: BlockConfig<TikTokResponse> = {
77
type: 'tiktok',
88
name: 'TikTok',
9-
description: 'Access TikTok user profiles and videos',
9+
description: 'Access TikTok user profiles, videos, and publish content',
1010
authMode: AuthMode.OAuth,
1111
longDescription:
12-
'Integrate TikTok into your workflow. Get user profile information including follower counts and video statistics. List and query videos with cover images, embed links, and metadata.',
12+
'Integrate TikTok into your workflow. Get user profile information including follower counts and video statistics. List and query videos with cover images, embed links, and metadata. Publish videos directly to TikTok from public URLs.',
1313
docsLink: 'https://docs.sim.ai/tools/tiktok',
1414
category: 'tools',
1515
bgColor: '#000000',
@@ -24,6 +24,9 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
2424
{ label: 'Get User Info', id: 'get_user' },
2525
{ label: 'List Videos', id: 'list_videos' },
2626
{ label: 'Query Videos', id: 'query_videos' },
27+
{ label: 'Query Creator Info', id: 'query_creator_info' },
28+
{ label: 'Direct Post Video', id: 'direct_post_video' },
29+
{ label: 'Get Post Status', id: 'get_post_status' },
2730
],
2831
value: () => 'get_user',
2932
},
@@ -87,9 +90,88 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
8790
value: 'query_videos',
8891
},
8992
},
93+
94+
// Direct Post Video specific fields
95+
{
96+
id: 'videoUrl',
97+
title: 'Video URL',
98+
type: 'short-input',
99+
placeholder: 'https://example.com/video.mp4',
100+
condition: {
101+
field: 'operation',
102+
value: 'direct_post_video',
103+
},
104+
required: {
105+
field: 'operation',
106+
value: 'direct_post_video',
107+
},
108+
},
109+
{
110+
id: 'title',
111+
title: 'Caption',
112+
type: 'long-input',
113+
placeholder: 'Video caption with #hashtags and @mentions',
114+
condition: {
115+
field: 'operation',
116+
value: 'direct_post_video',
117+
},
118+
},
119+
{
120+
id: 'privacyLevel',
121+
title: 'Privacy Level',
122+
type: 'dropdown',
123+
options: [
124+
{ label: 'Public', id: 'PUBLIC_TO_EVERYONE' },
125+
{ label: 'Friends', id: 'MUTUAL_FOLLOW_FRIENDS' },
126+
{ label: 'Followers', id: 'FOLLOWER_OF_CREATOR' },
127+
{ label: 'Only Me', id: 'SELF_ONLY' },
128+
],
129+
value: () => 'PUBLIC_TO_EVERYONE',
130+
condition: {
131+
field: 'operation',
132+
value: 'direct_post_video',
133+
},
134+
},
135+
{
136+
id: 'disableComment',
137+
title: 'Disable Comments',
138+
type: 'dropdown',
139+
options: [
140+
{ label: 'No', id: 'false' },
141+
{ label: 'Yes', id: 'true' },
142+
],
143+
value: () => 'false',
144+
condition: {
145+
field: 'operation',
146+
value: 'direct_post_video',
147+
},
148+
},
149+
150+
// Get Post Status specific fields
151+
{
152+
id: 'publishId',
153+
title: 'Publish ID',
154+
type: 'short-input',
155+
placeholder: 'v_pub_file~v2-1.123456789',
156+
condition: {
157+
field: 'operation',
158+
value: 'get_post_status',
159+
},
160+
required: {
161+
field: 'operation',
162+
value: 'get_post_status',
163+
},
164+
},
90165
],
91166
tools: {
92-
access: ['tiktok_get_user', 'tiktok_list_videos', 'tiktok_query_videos'],
167+
access: [
168+
'tiktok_get_user',
169+
'tiktok_list_videos',
170+
'tiktok_query_videos',
171+
'tiktok_query_creator_info',
172+
'tiktok_direct_post_video',
173+
'tiktok_get_post_status',
174+
],
93175
config: {
94176
tool: (inputs) => {
95177
const operation = inputs.operation || 'get_user'
@@ -99,6 +181,12 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
99181
return 'tiktok_list_videos'
100182
case 'query_videos':
101183
return 'tiktok_query_videos'
184+
case 'query_creator_info':
185+
return 'tiktok_query_creator_info'
186+
case 'direct_post_video':
187+
return 'tiktok_direct_post_video'
188+
case 'get_post_status':
189+
return 'tiktok_get_post_status'
102190
default:
103191
return 'tiktok_get_user'
104192
}
@@ -126,6 +214,23 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
126214
? inputs.videoIds.split(',').map((id: string) => id.trim())
127215
: [],
128216
}
217+
case 'query_creator_info':
218+
return {
219+
accessToken: credential,
220+
}
221+
case 'direct_post_video':
222+
return {
223+
accessToken: credential,
224+
videoUrl: inputs.videoUrl || '',
225+
privacyLevel: inputs.privacyLevel || 'PUBLIC_TO_EVERYONE',
226+
...(inputs.title && { title: inputs.title }),
227+
...(inputs.disableComment === 'true' && { disableComment: true }),
228+
}
229+
case 'get_post_status':
230+
return {
231+
accessToken: credential,
232+
publishId: inputs.publishId || '',
233+
}
129234
default:
130235
return {
131236
accessToken: credential,
@@ -141,6 +246,11 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
141246
maxCount: { type: 'number', description: 'Maximum number of videos to return (1-20)' },
142247
cursor: { type: 'number', description: 'Pagination cursor from previous response' },
143248
videoIds: { type: 'string', description: 'Comma-separated list of video IDs to query' },
249+
videoUrl: { type: 'string', description: 'Public URL of the video to post' },
250+
title: { type: 'string', description: 'Video caption/description' },
251+
privacyLevel: { type: 'string', description: 'Privacy level for the video' },
252+
disableComment: { type: 'string', description: 'Whether to disable comments' },
253+
publishId: { type: 'string', description: 'Publish ID to check status for' },
144254
},
145255
outputs: {
146256
// Get User outputs
@@ -155,7 +265,27 @@ export const TikTokBlock: BlockConfig<TikTokResponse> = {
155265
isVerified: { type: 'boolean', description: 'Whether account is verified' },
156266
// List/Query Videos outputs
157267
videos: { type: 'json', description: 'Array of video objects' },
158-
cursor: { type: 'number', description: 'Cursor for next page' },
159268
hasMore: { type: 'boolean', description: 'Whether more videos are available' },
269+
// Query Creator Info outputs
270+
creatorAvatarUrl: { type: 'string', description: 'Creator avatar URL' },
271+
creatorUsername: { type: 'string', description: 'Creator username' },
272+
creatorNickname: { type: 'string', description: 'Creator nickname' },
273+
privacyLevelOptions: { type: 'json', description: 'Available privacy levels for posting' },
274+
commentDisabled: { type: 'boolean', description: 'Whether comments are disabled by default' },
275+
duetDisabled: { type: 'boolean', description: 'Whether duets are disabled by default' },
276+
stitchDisabled: { type: 'boolean', description: 'Whether stitches are disabled by default' },
277+
maxVideoPostDurationSec: { type: 'number', description: 'Max video duration in seconds' },
278+
// Direct Post Video outputs
279+
publishId: { type: 'string', description: 'Publish ID for tracking post status' },
280+
// Get Post Status outputs
281+
status: {
282+
type: 'string',
283+
description: 'Post status (PROCESSING_DOWNLOAD, PUBLISH_COMPLETE, FAILED)',
284+
},
285+
failReason: { type: 'string', description: 'Reason for failure if status is FAILED' },
286+
publiclyAvailablePostId: {
287+
type: 'json',
288+
description: 'Array of public post IDs when published',
289+
},
160290
},
161291
}

apps/sim/lib/auth/auth.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2502,7 +2502,13 @@ export const auth = betterAuth({
25022502
clientSecret: env.TIKTOK_CLIENT_SECRET as string,
25032503
authorizationUrl: 'https://www.tiktok.com/v2/auth/authorize/',
25042504
tokenUrl: 'https://open.tiktokapis.com/v2/oauth/token/',
2505-
scopes: ['user.info.basic', 'user.info.profile', 'user.info.stats', 'video.list'],
2505+
scopes: [
2506+
'user.info.basic',
2507+
'user.info.profile',
2508+
'user.info.stats',
2509+
'video.list',
2510+
'video.publish',
2511+
],
25062512
responseType: 'code',
25072513
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/tiktok`,
25082514
getUserInfo: async (tokens) => {

apps/sim/lib/oauth/oauth.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,11 +803,17 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
803803
services: {
804804
tiktok: {
805805
name: 'TikTok',
806-
description: 'Access TikTok user profiles and videos.',
806+
description: 'Access TikTok user profiles, videos, and publish content.',
807807
providerId: 'tiktok',
808808
icon: TikTokIcon,
809809
baseProviderIcon: TikTokIcon,
810-
scopes: ['user.info.basic', 'user.info.profile', 'user.info.stats', 'video.list'],
810+
scopes: [
811+
'user.info.basic',
812+
'user.info.profile',
813+
'user.info.stats',
814+
'video.list',
815+
'video.publish',
816+
],
811817
},
812818
},
813819
defaultService: 'tiktok',

apps/sim/tools/registry.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,14 @@ import {
16251625
} from '@/tools/telegram'
16261626
import { textractParserTool } from '@/tools/textract'
16271627
import { thinkingTool } from '@/tools/thinking'
1628-
import { tiktokGetUserTool, tiktokListVideosTool, tiktokQueryVideosTool } from '@/tools/tiktok'
1628+
import {
1629+
tiktokDirectPostVideoTool,
1630+
tiktokGetPostStatusTool,
1631+
tiktokGetUserTool,
1632+
tiktokListVideosTool,
1633+
tiktokQueryCreatorInfoTool,
1634+
tiktokQueryVideosTool,
1635+
} from '@/tools/tiktok'
16291636
import { tinybirdEventsTool, tinybirdQueryTool } from '@/tools/tinybird'
16301637
import {
16311638
trelloAddCommentTool,
@@ -2735,6 +2742,9 @@ export const tools: Record<string, ToolConfig> = {
27352742
tiktok_get_user: tiktokGetUserTool,
27362743
tiktok_list_videos: tiktokListVideosTool,
27372744
tiktok_query_videos: tiktokQueryVideosTool,
2745+
tiktok_query_creator_info: tiktokQueryCreatorInfoTool,
2746+
tiktok_direct_post_video: tiktokDirectPostVideoTool,
2747+
tiktok_get_post_status: tiktokGetPostStatusTool,
27382748
clay_populate: clayPopulateTool,
27392749
clerk_list_users: clerkListUsersTool,
27402750
clerk_get_user: clerkGetUserTool,

0 commit comments

Comments
 (0)